主要內容
- 爲什麼要使用Handler
- 什麼是Handler
- 如何使用Handler
- 在子線程中創建Handler
爲什麼要使用Handler
子線程不允許訪問UI,UI操作必須在UI線程,也就是主線程中執行。
Android UI是線程不安全的,要想在子線程中更新UI,必須通過線程間通信,可以使用Handler,AsyncTask,runonUiThread等來實現。
什麼是Handler
Handler允許發送和處理與MessageQueue相關的Message和Runnable對象,每個Handler實例都與一個線程和這個線程的MessageQueue相關聯。當你new一個Handler時,它將綁定到創建他的Thread/MessageQueue上,然後它將向MessageQueue上發送消息或Runnable對象,並在他們從MessageQueue中釋放時執行他們。
它實際上是一種消息循環處理機制。其中Handler,Looper都是在當前線程,也就是創建Handler的線程。
如何使用Handler
常用用法
新寫一個類繼承Handler
private static class MyHandler extends Handler {
private Context context;
private MyHandler(Context context) {
this.context = context;
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
Toast.makeText(context, "hha", Toast.LENGTH_SHORT).show();
break;
default:
break;
}
}
}
創建Handler實例
private MyHandler handler = new MyHandler(this);
這裏需要注意的是:靜態內部類只能訪問外部類的靜態屬性和方法,非靜態內部類會持有外部類的引用,可能會造成內存泄露。如果Handler在主線程中創建,那麼它自動綁定主線程。Looper在哪個線程,Handler就在哪個線程處理消息。
模擬子線程發送消息
new Thread(new Runnable() {
@Override
public void run() {
Message message = Message.obtain();
message.what = 1; // 標誌位,可以區分不同類型的消息,類比type
message.obj = 2; // 傳遞的對象
handler.sendMessage(message);
}
}).run();
與Handler一樣,這裏Thread同樣存在可能造成內存泄漏的問題,但是偷個懶,這裏暫時不做處理。
Toast
因爲在主線程中纔可以展示Toast,爲了防止代碼中出現子線程展示Toast的情況,我們可以在工具類中對Toast做處理
private static Handler handler;
public static void showToast(final Context context, final String msg) {
if (context == null || TextUtils.isEmpty(msg)) {
return;
}
if (handler == null) {
handler = new Handler(Looper.getMainLooper());
}
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(context, msg, LENGTH_SHORT).show();
}
});
}
在子線程中創建Handler?
上面是關於主線程的Handler,也就是子線程通知主線程,那麼主線程如何通知子線程呢?
仿照子線程通知主線程,我們將子線程的Looper設置到Handler中去
private static class MyThread extends Thread {
/**
* 取出子線程的Looper
*/
private Looper looper;
public Looper getLooper() {
return looper;
}
@Override
public void run() {
super.run();
// 創建子線程的Looper
Looper.prepare();
// 取出該子線程的Looper
looper = Looper.myLooper();
// 只有調用下面的方法纔可以循環取用消息
Looper.loop();
}
}
初始化對象
private MyThread thread;
private Handler handler;
調用
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
thread = new MyThread();
thread.start();
handler = new Handler(thread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
handler.sendEmptyMessage(0);
}
但是這樣會報空指針異常,爲什麼呢?因爲在OnCreate()方法中,子線程執行之後,下面的代碼也會依此執行,可能出現的情況就是Looper還沒來得及初始化,就直接在Handler中使用了,那麼就會出現空指針異常。也可能不會出現異常,但這都是隨機的。
我們可以使用這種方式在子線程中創建Handler
private static class LooperThread extends Thread {
private Handler handler;
public Handler getHandler() {
return handler;
}
@Override
public void run() {
super.run();
Looper.prepare();
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 處理消息
Log.i("TAG","hha");
}
};
Looper.loop();
}
}
但是需要注意的是,Handler可能爲空
HandlerThread
上面的這種方式過於麻煩,我們可以使用HandlerThread。
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
HandlerThread handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 這裏是子線程,可以處理耗時操作
// 處理消息
}
};
handler.sendEmptyMessage(0);
}
HandlerThread還可用於多任務下載引用
https://www.jianshu.com/p/0a274564a4b1
https://blog.csdn.net/iispring/article/details/47115879
https://developer.android.com/reference/android/os/Handler
https://blog.csdn.net/chenxiaofeng_/article/details/51492764
https://www.cnblogs.com/lang-yu/p/6228832.html
https://blog.csdn.net/u011240877/article/details/72905631#handlerthread-%E7%AE%80%E4%BB%8B