在Android開發中,Handler機制是一個很重要的知識點,主要作用是消息通信。
Handler基本原理
Handler主要用于異步消息的處理:當發出一個消息之後,首先進入一個消息隊列,發送消息的函數即刻傳回,而另外一個部分在消息隊列中逐一将消息取出,然後對消息進行處理,也就是發送消息和接收消息不是同步的處理。 這種機制通常用來處理相對耗時比較長的操作。
先來說一下流程
在ActivityThread中通過prepareMainLooper()建立looper,并通過threadLocal将目前線程與Looper綁定【一個線程綁定一個Looper】,同時也建立了MessageQueue【一個Looper維護一個MessageQueue隊列】。
然後Looper.loop()開啟死循環,當沒有message時,循環是阻塞的
handler在子線程中發送了一條消息,将handler執行個體指派給target,并将消息按時間的順序插入到MessageQueue隊列中,循環會被喚醒,looper取出消息,通過target開始分發到對應的handler回調方法中【一個線程可以對應多個Handler】
各種類的含義
Message:消息,其中包含了消息ID,消息處理對象以及處理的資料等,由MessageQueue統一列隊,終由Handler處理。
Handler:處理者,負責Message的發送及處理。使用Handler時,需要實作handleMessage(Message msg)方法來對特定的Message進行處理,例如更新UI等。
MessageQueue:消息隊列,用來存放Handler發送過來的消息,并按照FIFO規則執行。當然,存放Message并非實際意義的儲存,而是将Message以連結清單的方式串聯起來的,等待Looper的抽取。
Looper:消息泵,不斷地從MessageQueue中抽取Message執行。是以,一個MessageQueue需要一個Looper。
Thread:線程,負責排程整個消息循環,即消息循環的執行場所
可以把handler基本工作原理看作一個傳送履帶,HandlerMessage發送消息,消息内容會往MessageQueue消息隊列中添加,同時開啟線程,一個線程對應一個Looper,Looper.loop()方法會無限循環調用MessageQueue的next()方法來擷取新消息,而next是是一個阻塞操作,但沒有資訊時,next方法會一直阻塞在那裡,這也導緻loop方法一直阻塞在那裡。如果MessageQueue的next方法傳回了新消息,Looper就會處理這條消息。最後handler接收處理消息。
Handler的簡單使用
我們一般使用handler發送消息,隻需要兩步,首先是建立一個Handler對象,并重寫handleMessage方法,就是上圖中的3(Message.target.handleMeesage),然後需要消息通信的地方,通過Handler的sendMessage方法發送消息(這裡我們建立了一個子線程,模拟子線程向主線程發送消息)。
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
private Handler mHandler;
private Button btnSendeToMainThread;
private static final int MSG_SUB_TO_MAIN= 100;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 1.建立Handler,并重寫handleMessage方法
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 處理消息
switch (msg.what) {
case MSG_SUB_TO_MAIN:
// 列印出處理消息的線程名和Message.obj
Log.e(TAG, "接收到消息: " + Thread.currentThread().getName() + ","+ msg.obj);
break;
default:
break;
}
}
};
btnSendeToMainThread = (Button) findViewById(R.id.btn_sendto_mainthread);
btnSendeToMainThread .setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 建立一個子線程,在子線程中發送消息
new Thread(new Runnable() {
@Override
public void run() {
Message msg = Message.obtain();
msg.what = MSG_SUB_TO_MAIN;
msg.obj = "這是一個來自子線程的消息";
// 2.發送消息
mHandler.sendMessage(msg);
}
}).start();
}
});
}
}
代碼如下:點選了發送按鈕,看log:
Looper.prepare()
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
// 規定了一個線程隻有一個Looper,也就是一個線程隻能調用一次Looper.prepare()
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 如果目前線程沒有Looper,那麼就建立一個,存到sThreadLocal中
sThreadLocal.set(new Looper(quitAllowed));
}
從上面的代碼可以看出,一個線程最多隻有一個Looper對象。當沒有Looper對象時,去建立一個Looper,并存放到sThreadLocal中,sThreadLocal是一個static的ThreadLocal對象,關于它的詳細使用,以後有機會再介紹,這裡隻要知道,它存儲了Looper對象的副本,并且可以通過它取得目前線程在之前存儲的Looper的副本。如下圖:
接下來看Looper的構造方法:
private Looper(boolean quitAllowed) {
// 建立了MessageQueue,并供Looper持有
mQueue = new MessageQueue(quitAllowed);
// 讓Looper持有目前線程對象
mThread = Thread.currentThread();
}
這裡主要就是建立了消息隊列MessageQueue,并讓它供Looper持有,因為一個線程最大隻有一個Looper對象,是以一個線程最多也隻有一個消息隊列。然後再把目前線程指派給mThread。
MessageQueue的構造方法沒有什麼可講的,它就是一個消息隊列,用于存放Message。
是以Looper.prepare()的作用主要有以下三點
- 建立Looper對象
- 建立MessageQueue對象,并讓Looper對象持有
- 讓Looper對象持有目前線程
new Handler()
Handler有很多構造方法,主要是提供自定義Callback、Looper等,我們先從最簡單的無參構造方法看起:
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
// 不相關代碼
......
//得到目前線程的Looper,其實就是調用的sThreadLocal.get
mLooper = Looper.myLooper();
// 如果目前線程沒有Looper就報運作時異常
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
// 把得到的Looper的MessagQueue讓Handler持有
mQueue = mLooper.mQueue;
// 初始化Handler的Callback,其實就是最開始圖中的回調方法的2
mCallback = callback;
mAsynchronous = async;
}
首先,調用了Looper.myLooper,其實就是調用sThreadLocal.get方法,會得到目前線程調用sThreadLocal.set儲存的Looper對象,讓Handler持有它。接下來就會判斷得到的Looper對象是否為空,如果為空,就會報
"Can't create handler inside thread that has not called Looper.prepare(),這不就是我們之前在沒有調用Looper.prepare就在子線程中建立Handler時報的錯誤嘛。的确,當我們沒有調用Looper.prepare(),則目前線程中是沒有Looper對象的。
然後,讓Handler持有得到的Looper對象的MessageQueue和設定處理回調的Callback對象(最開始圖中的回調方法2)。
到這裡,預設的Handler的建立過程就結束了,主要有以下幾點
- 建立Handler對象
- 得到目前線程的Looper對象,并判斷是否為空
- 讓建立的Handler對象持有Looper、MessageQueu、Callback的引用
(檢視更多(出處))