前言
今天我們來回顧複習下Handler,處於性能優化的考慮,Android的UI線程是線程不安全的。爲了避免多個線程併發操作UI的引發UI顯示錯亂問題,Android指定只允許在UI線程修改更新UI組件。其他線程更新UI拋出android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.異常,當我們在子線程做完耗時操作時,需要更新UI時,我們就需要通過Handler來實現啦。
1、Handler是什麼?
1、Handler通過發送和處理Message和Runnable對象來關聯相對應線程的MessageQueue。
2、線程之間通信。
2、Handler使用方法
post(runnable)
sendMessage(message)
3、Handler工作原理
- Handler: 負責發送消息和處理消息。
- Looper: 負責管理MessageQueue,從MessageQueue中取出消息,交給消息對應的Handler處理。一個線程只有一個Looper對象。
- Message: Handler接收處理的消息對象。
- MessageQueue: 消息隊列,負責管理Message, 先進先出。程序創建Looper對象時,會在他的構造器裏創建MessageQueue對象。
總結:
1、首先Looper.prepare()在本線程中保存一個Looper實例,然後該實例中保存一個MessageQueue對象;因爲Looper.prepare()在一個線程中只能調用一次,所以MessageQueue在一個線程中只會存在一個。
2、Looper.loop()會讓當前線程進入一個無限循環,不斷從MessageQueue的消息隊列中讀取消息,然後回調msg.target.dispatchMessage(msg)方法。
3、Handler的構造方法,會首先得到當前線程中保存的Looper實例,進而與Looper實例中的MessageQueue相關聯。
4、Handler的sendMessage方法,會給msg的target賦值爲handler自身,然後加入MessageQueue中。
5、在構造Handler實例時,我們會重寫handleMessage方法,也就是msg.target.dispatchMessage(msg)最終調用的方法。
Android 異步消息處理機制 讓你深入理解 Looper、Handler、Message三者關係
4、Handler引起的內存泄漏
原因:非靜態內部類持有外部類的匿名引用,導致Activity無法釋放。
解決:
- Handler內部持有外部Activity的弱引用。
- Handler改爲靜態內部類。
- Handler.removeCallback()。
5、代碼實踐
第1種在UI線程創建Handler,子線程發送消息,UI線程收到消息進行處理:
由於在Activity的啓動代碼中在當前UI線程調用了Looper.prepare()和Looper.loop()方法,所以我們在Activity創建Handler就可以了,詳情我們可以查看ActivityThread類的main 方法中,我們就會發現調用了Looper.prepareMainLooper()和Looper.loop()方法。
ActivityThread創建Looper的系統源碼:
public final class ActivityThread {
public static void main(String[] args) {
// 省略部分代碼
//prepareMainLooper()方法中調用了Looper.prepare(false); 創建出Looper對象
Looper.prepareMainLooper();
// 開始輪詢
Looper.loop();
// 如下代碼我們發現在UI線程我們不能手動調用停止Loop,不然會拋異常。
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
例子:在子線程發送消息在UI線程彈出Toast具體代碼:
package com.example.ling.review.handler;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;
import com.example.ling.review.R;
import java.lang.ref.WeakReference;
public class HandlerActivity extends AppCompatActivity {
private Handler mHandler = new MainHandler(this);
// Handler改爲靜態內部類,避免內存泄漏
public static class MainHandler extends Handler {
private WeakReference<HandlerActivity> mWeakReference = null;
public MainHandler(HandlerActivity activity) {
// 將Activity 包裝成弱引用,避免內存泄漏
mWeakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
// 接收處理消息
HandlerActivity activity = mWeakReference.get();
if (activity != null) {
Toast.makeText(activity, "我在UI線程上哦", Toast.LENGTH_SHORT).show();
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
initView();
}
private void initView() {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
// 第一種 post(runnable)用法
mHandler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "我在UI線程上哦", Toast.LENGTH_SHORT).show();
}
});
// 第二種 sendMessage(message)用法
Thread.sleep(2000);
Message message = Message.obtain();
mHandler.sendMessage(message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
@Override
protected void onDestroy() {
super.onDestroy();
// activity 銷燬時 Handler 移除所有回調和信息
if (mHandler != null) {
mHandler.removeCallbacksAndMessages(null);
mHandler = null;
}
}
}
第2種子線程創建Handler,在子線程發送消息,創建Handler的子線程中接收到消息進行處理:
1、子線程使用Handler需要自己創建Looper對象;
2、Looper創建完,在創建Handler對象;
3、開啓Looper輪詢,Handler就可以發送、處理消息啦;
例子:
public class HandlerActivity2 extends AppCompatActivity {
private ThreadHandler mHandler;
// Handler改爲靜態內部類,避免內存泄漏
public static class ThreadHandler extends Handler {
@Override
public void handleMessage(Message msg) {
// 接收處理消息
Log.d("TAG", "接收消息啦----- 當前線程爲:" + Thread.currentThread().getName());
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
newThread1();
newThread2();
}
/**
* 創建子線程1 創建Handler
*/
private void newThread1() {
new Thread(new Runnable() {
@Override
public void run() {
Log.d("TAG", "當前線程爲:" + Thread.currentThread().getName());
// 1、創建Looper
Looper.prepare();
// 2、創建Handler
mHandler = new ThreadHandler();
// 3、Looper開始輪詢
Looper.loop();
}
}).start();
}
/**
* 創建子線程2 用子線程1的handler 發送消息
*/
private void newThread2() {
new Thread(new Runnable() {
@Override
public void run() {
try {
Log.d("TAG", "當前線程爲:" + Thread.currentThread().getName());
Thread.sleep(2000);
Message message = Message.obtain();
mHandler.sendMessage(message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
@Override
protected void onDestroy() {
super.onDestroy();
// activity 銷燬時 Handler 移除所有回調和信息
if (mHandler != null) {
mHandler.removeCallbacksAndMessages(null);
mHandler = null;
}
}
}
運行打印結果如下:
我們發現在Thread-2子線程創建了Handler, 在Thread-3子線程發送消息,接收到消息在Thread-2 子線程。