0
0
Lập trình
Admin Team
Admin Teamtechmely

Hướng Dẫn Chi Tiết về Looper, MessageQueue và Handler trong Android

Đăng vào 3 tuần trước

• 6 phút đọc

Tổng Quan về Looper, MessageQueue và Handler trong Android

Trong phát triển ứng dụng Android, việc thực hiện các tác vụ nặng như request mạng hoặc đọc ghi database trực tiếp trên main thread có thể dẫn đến tình trạng đơ hoặc thậm chí là crash ứng dụng. Để đảm bảo trải nghiệm mượt mà cho người dùng, rất cần thiết để chuyển các tác vụ này xuống background thread, nhằm tránh làm tắc nghẽn main thread.

Ví dụ, khi người dùng nhấn nút Submit trên main thread, chức năng request mạng sẽ được thực hiện ở background thread. Khi có kết quả, hệ thống sẽ gửi kết quả quay lại main thread. Android cung cấp một số công cụ như Looper, MessageQueueHandler để hỗ trợ việc thực thi song song các tác vụ và truyền thông điệp giữa các thread.

Tại Sao Không Dùng Kotlin Coroutines?

Hiện nay, rất nhiều lập trình viên ưu tiên sử dụng Kotlin Coroutines để thực hiện các use case tương tự. Tuy nhiên, trong một số dự án đặc thù như Android Automotive, bộ ba Looper, MessageQueue, và Handler vẫn là lựa chọn phổ biến để giải quyết vấn đề concurrency.

Bài viết này sẽ giải thích chi tiết vai trò và nhiệm vụ của từng thành phần trong ba bộ công cụ này, cùng với cách thức chúng tương tác với nhau. Do sự liên quan chéo của các thành phần này, trong quá trình giải thích, có thể sẽ cần nhắc đến những thành phần khác. Nếu bạn không hiểu ngay, hãy cứ tạm bỏ qua, sau khi đọc hết bài, bạn có thể quay lại để hiểu rõ hơn.

Looper

Looper là một lớp (class) quản lý message loop cho một thread. Mỗi thread chỉ có duy nhất một Looper. Theo mã nguồn của Android SDK, lớp Looper được định nghĩa như sau:

java Copy
public final class Looper {
    final MessageQueue mQueue;
    final Thread mThread;

    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Chỉ có một Looper được tạo cho mỗi thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
}

Mặc định, một thread không liên kết với bất kỳ message loop nào. Để tạo một message loop, bạn cần gọi phương thức Looper.prepare(). Sau đó, bạn tiếp tục gọi phương thức Looper.loop() để xử lý các message cho đến khi vòng lặp dừng lại.

Ví Dụ Triển Khai Looper

java Copy
class LooperExampleThread extends Thread {
    public void run() {
        Looper.prepare();
        Looper.loop();
    }
}

MessageQueue

MessageQueue là một cấu trúc dữ liệu chứa danh sách các MessageRunnable objects cần được xử lý, hoạt động theo nguyên tắc FIFO (First-In-First-Out). Bạn có thể truy cập MessageQueue của thread hiện tại qua phương thức Looper.myQueue().

Mỗi Looper chỉ có một MessageQueue duy nhất. Message không được thêm trực tiếp vào MessageQueue mà phải thông qua Handler. Looper là người liên tục trích xuất và xử lý messages từ queue.

Handler

Handler là lớp dùng để gửi và xử lý Message cũng như Runnable objects liên kết với MessageQueue của một thread. Mỗi Handler chỉ liên kết với một thread và message queue của nó.

Khi khởi tạo một Handler, bạn phải truyền vào constructor của nó một Looper. Một ví dụ tiêu biểu như sau:

java Copy
public Handler(@NonNull Looper looper) {}

Các message sẽ được thực thi trên thread mà chiếc Looper này danh sách thuộc về.

Phương Thức Thường Dùng Trong Handler

Một số phương thức phổ biến của Handler bao gồm:

  • post(Runnable)
  • postAtTime(java.lang.Runnable, long)
  • postDelayed(Runnable, Object, long)
  • sendEmptyMessage(int)
  • sendMessage(Message)
  • sendMessageAtTime(Message, long)
  • sendMessageDelayed(Message, long)

Handler có hai chức năng chính:

  1. Lên lịch thực hiện các message và runnable trong tương lai.
  2. Thực hiện tác vụ trên một thread khác với thread hiện tại.

Quy Trình Giao Tiếp Giữa Các Thành Phần

  1. Khi một Message hoặc Runnable được gửi qua Handler, nó được đưa vào MessageQueue.
  2. Looper sẽ liên tục kiểm tra MessageQueue để tìm message mới.
  3. Khi có message, Looper sẽ trích xuất nó từ queue và gửi đến Handler tương ứng để xử lý.
  4. Handler sẽ xử lý message trong thread mà nó đã liên kết.

Ví Dụ Minh Họa

java Copy
class ProcessingThread extends Thread {
    public Handler mHandler;

    public void run() {
        Looper.prepare();
        mHandler = new Handler(Looper.myLooper()) {
            public void handleMessage(Message msg) {
                // Xử lý các message nhận được
            }
        };
        Looper.loop();
    }
}

class ClientThread extends Thread {
    private void sendMessageExample() {
        Message msg = Message.obtain(mHandler, 1);
        msg.obj = "Tin nhắn mới";
        mHandler.sendMessage(msg);
    }

    private void sendRunnableExample() {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                // Tác vụ thực thi trên ProcessingThread
            }
        });
    }
}

Sử Dụng HandlerThread

Trong thực tiễn, ít khi ai tạo ra Thread và quản lý Looper một cách thủ công như ví dụ ở trên. Android đã cung cấp cho chúng ta HandlerThread, một loại thread đặc biệt chứa property Looper. Chúng ta có thể lấy Looper bằng phương thức getLooper() trong HandlerThread:

java Copy
HandlerThread thread = new HandlerThread("ProcessingThread");
thread.start();
Looper looper = thread.getLooper();
Handler handler = new Handler(looper);

Main Thread trong Android

Main thread (UI thread) trong Android đã có sẵn một Looper, bạn có thể lấy nó thông qua phương thức Looper.getMainLooper(). Ví dụ điển hình là chúng ta thường tạo một Handler để thực hiện một tác vụ trên UI thread sau một khoảng thời gian nhất định:

java Copy
Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
    @Override
    public void run() {
        // Tác vụ bị delay
    }
}, 3000);

Kết Luận

Looper, MessageQueue, và Handler là ba thành phần quan trọng trong hệ thống xử lý bất đồng bộ của Android. Sự kết hợp của chúng đảm bảo việc giao tiếp giữa các thread diễn ra một cách hiệu quả và an toàn. Việc hiểu rõ cách hoạt động của ba thành phần này sẽ giúp bạn phát triển các ứng dụng Android chất lượng hơn. Cảm ơn bạn đã theo dõi bài viết này!
source: viblo

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào