Looper, Handler and HandlerThread in android
Bài đăng này đã không được cập nhật trong 3 năm
MultiThreading
và Task running
là những khái niệm quen thuộc trong lập trình. Trong Java java.util.concurrent là package chứa các utility class giúp ích trong việc lập trình song song (concurrent programming) và Fork/Join framework là một framework hiện thực của ExecutorService interface
giúp tận dụng tối đa khả năng của bộ đa xử lý multiple processors. Trong Android, Looper
, Handler
và HandlerThread
là cách giải quyết các vấn đề liên quan tới lập trình bất đồng bộ.
Đặt vấn đề
Trong java, Thread thông thường mang ý nghĩa chỉ sử dụng một lần nghĩa là khởi tạo và chết đi sau khi thực thi hàm run()
.
Bản thân Thread là con dao 2 lưỡi, chúng ta có thể tăng tốc việc thực thi bằng cách phân phối task
giữa các Thread, tuy nhiên, ngược lại có thể làm chậm việc thực thi các task
khi mà số lượng Thread vượt quá số lượng có thể.
Vì vậy cách tốt nhất là phải có 1 số lượng tối ưu Thread và tái sử dụng chúng cho việc thực thi task
. (Tham khảo thêm Sử dụng ThreadPoolExecutor trong android). Trong bài viết này, chúng ta sẽ cùng nhau tái sử dụng Thread
với Looper
và Handler
.
Tái sử dụng Thread
- Giữ Thread hoạt động bởi 1 vòng lặp trong hàm
run()
của nó dùngLooper
- Các task được thực thi nối tiếp nhau bởi Thread và được duy trì trong một hàng đợi (
MessageQueue
) - Hủy
Thread
sau khi hoàn thành.
Looper, Handler, HandlerThread
Hệ thống Android trãi qua các bước:
MessageQueue
là hàng đợi chứa nhiệm vụ cần được thực thi.Handler
thực hiện enqueues task từMessageQueue
dùngLooper
và đồng thời thực thi chúng khi nhận từMessageQueue
.Looper
là 1worker
giữ choThread
tồn tại, nó sẽ vòng quaMessageQueue
và gởi cácmessage
tới cácHandler
tương ứng để thực thi.- Cuối cùng
Thread
bị hủy từ lời gọi hàmquit()
củaLooper
.
Mỗi Thread chỉ có thể có một
Looper
duy nhất và có thế có nhiềuHandler
liên kết với nó.
Tạo Looper và MessageQueue cho Thread
Cách tự tạo và tự quản lý:
Với cách này, bạn sẽ tự tạo 1 Thread với Looper
và quản lý chúng. Mặc định, Handler
sẽ liên kết ngầm (implicitly) với Thread mà nó đc khởi tạo qua Looper
, tuy nhiên bạn có thể buộc nó vào 1 Thread khác bằng cách cung cấp rõ trong constructor
lúc khởi tạo.
class LooperThread extends Thread {
public Handler handler;
public void run() {
// Creating Looper and MessageQueue for this Thread
Looper.prepare();
handler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
// this will run in non-ui/background thread
}
};
// Associate the Thread with its Looper and MessageQueue
Looper.loop();
}
}
- Thread nhận 1
Looper
vàMessageQueue
bằng cách gọiLooper.prepare()
ngay khi bắt đầu thực thi (run()
). Looper.prepare()
sẽ xác định Thread đang gọi, tạo 1 đối tượngLooper
vàMessageQueue
và kết nối chúng với Thread bên trongThreadLocal
.Looper.loop()
cần được gọi để bắt đầu kết nối.
Trong ví dụ trên, hãy cùng xem xét cách tạo Handler
cho Thread và cách mà Handler
nhận, gởi message:
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
// process incoming messages here
// this will run in the thread, which instantiates it
}
};
Handler
gởi message tới MessageQueue
bởi Handler
có thể thông qua 2 cách:
Message
: là class chứa các phương thức hữu ích khác nhau để gởi dữ liệu.
Message msg = Message.obtain();
msg.obj = "Send message From Me";
handler.sendMessage(msg);
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
// this will run in the main thread
}
});
Ở ví dụ này, chúng ta tạo một Handler
và cung cấp Looper
liên kết với Main Thread
. Khi post Runnable, nó sẽ được thêm vào MessageQueue
của Main Thread
và sau đó được thực thi trong Main Thread
.
Tự tạo một Thread và cung cấp Looper
, MessageQueue
đôi khi gây khó khăn cho lập trình viên. Vì vậy, Android cung cấp cho chúng ta HandlerThread
(kế thừa từ Thread) để đơn giản hóa việc thực thi task. Bên trong nó là những thứ tương tự mà chúng ta đã làm ở trên nhưng đã được tối ưu. Do vậy, hãy nên sử dụng HandlerThread
.
Cách tạo dùng HandlerThread
private class MyHandlerThread extends HandlerThread {
Handler handler;
public MyHandlerThread(String name) {
super(name);
}
@Override
protected void onLooperPrepared() {
// Only instantiates the Handler when looper is prepared
// So, Handler can be associated with that Looper
handler = new Handler(getLooper()) {
@Override
public void handleMessage(Message msg) {
// process incoming messages here
// this will run in non-ui/background thread
}
};
}
}
Note:
Looper
chỉprepared
sau khi hàmstart()
củaHandlerThread
được thực thi.- Một
Handler
có thể được liên kết với mộtHandlerThread
chỉ khiLooper
của nó đượcprepared
. HandlerThread
cần gọi hàmquit()
để giải phóng resource và dừng quá trình thực thi.
Một cách khác để khởi tạo HandlerThread
HandlerThread handlerThread = new HandlerThread("MyHandlerThread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());
Tham khảo
https://blog.mindorks.com/android-core-looper-handler-and-handlerthread-bd54d69fe91a#.kc15r6z0b
https://blog.nikitaog.me/2014/10/11/android-looper-handler-handlerthread-i/
https://blog.nikitaog.me/2014/10/11/android-looper-handler-handlerthread-ii/
All rights reserved