Android訊息機制---Handler工作原理
簡述:
子執行緒沒有辦法對UI介面上的內容進行操作,如果操作,將丟擲異常:CalledFromWrongThreadException為了實現子執行緒中操作UI介面,Android中引入了Handler訊息傳遞機制。
什麼是Handler?
handler通俗一點講就是用來在各個執行緒之間傳送資料的處理物件。在任何執行緒中,只要獲得了另一個執行緒的handler,則可以通過 handler.sendMessage(message)方法向那個執行緒傳送資料。基於這個機制,我們在處理多執行緒的時候可以新建一個thread,這個thread擁有UI執行緒中的一個handler。當thread處理完一些耗時的操作後通過傳遞過來的handler向UI執行緒傳送資料,由UI執行緒去更新介面。
handler可以分發Message物件和Runnable物件到主執行緒中, 每個Handler例項,都會繫結到建立他的執行緒中(一般是位於主執行緒),它有兩個作用:
(1)安排Message或Runnable 在某個主執行緒中某個地方執行;(2)安排一個動作在不同的執行緒中執行。
handler執行緒間通訊示意圖
基本使用:
1、子執行緒向主執行緒傳送訊息Message
Handler的一種經典的使用方法,也是更新UI最常用的方法。
程式碼:
import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends AppCompatActivity { private Button button; private TextView txt; private static final int WORKTHREAD = 1; private Handler mainThreadHandler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == WORKTHREAD) { final String data = (String) msg.obj; txt.setText(data); Toast.makeText(MainActivity.this, data, Toast.LENGTH_SHORT).show(); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button = (Button) findViewById(R.id.btn); txt = (TextView) findViewById(R.id.txt); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //開啟一個工作執行緒 new Thread(new Runnable() { @Override public void run() { Message message = Message.obtain(); message.what = WORKTHREAD; message.obj = "來自子執行緒資料!"; mainThreadHandler.sendMessage(message); } }).start(); } }); } }
2、主執行緒向子執行緒傳送訊息
在平時的開發中有時會碰到主執行緒子執行緒傳送訊息,這時候來考慮這個問題。
程式碼:
public class MainActivity extends AppCompatActivity {
private TextView txt;
private Button btn_main_start;
Handler workHandler;
private static final int MAIN_THREAD = 2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intiViews();
//開啟子執行緒接收主執行緒資料
final WorkThread workThread = new WorkThread();
workThread.start();
btn_main_start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//主執行緒傳送資料
Message message = Message.obtain();
message.what = MAIN_THREAD;
message.obj = "來自主執行緒資料!";
workHandler.sendMessage(message);
}
});
}
private void intiViews() {
txt = (TextView) findViewById(R.id.txt);
btn_main_start = (Button) findViewById(R.id.btn_main_start);
}
class WorkThread extends Thread {
public void run() {
Looper.prepare();
workHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
if (msg.what == MAIN_THREAD) {
final String data = (String) msg.obj;
txt.setText(data);
Toast.makeText(MainActivity.this, data, Toast.LENGTH_SHORT).show();
}
}
};
Looper.loop();
}
}
}
從上面的程式碼中=可能會有一些疑問,比如:
(1)為什麼主執行緒向子執行緒傳送訊息時要呼叫手動下列程式碼:
Looper.prepare();
workHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
//do something
}
};
Looper.loop();
(2)為什麼new Handler()構造方法中沒有使用當前執行緒的Looper而是使用了主執行緒Looper?
如果子執行緒涉及到更新UI的操作需要使用主執行緒的Looper,如果沒有涉及到跟新UI的操作而只是簡單的處理訊息可以使用當前執行緒的Looper.
為了對Handler作進一步的瞭解,接下來我們分析一下原始碼。
原始碼分析:
先來看一下Handler的構造方法:
Public constructors | |
---|---|
Default constructor associates this handler with the for the current thread. | |
Constructor associates this handler with the for the current thread and takes a callback interface in which you can handle messages. | |
Use the provided instead of the default one. | |
Use the provided instead of the default one and take a callback interface in which to handle messages. |
我們開啟原始碼就會發現Handler(),Handler(Handler.Callbackcallback)以及Handler(boolean async)最終都會呼叫下面這個構造方法:
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
//如果Handler為匿名內部類、區域性內部類或者非靜態成員內部類(即除了靜態內部類外的所有內部類)立即警告“潛在的記憶體洩漏”
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
//沒有指定Looper,預設使用當前執行緒的Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
這個構造方法中做了兩件事: ①檢測“潛在的記憶體洩漏”(與該博文無太多關係,如果想了解更多請看:Handler引發記憶體洩漏)②關聯當前執行緒的Looper.
再看其他兩個構造方法:Handler(Looper looper)和Handler(Looper looper, Callback callback)。這兩個構造方法最終呼叫的是下面的構造方法:
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
從以上兩段程式碼可以看出:Handler構造方法需要先關聯一個Looper,
接下來再來探一探sendMessage(Message msg)方法:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
該方法最後呼叫了sendMessageDelayed()方法,我們找到該方法:
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
該方法呼叫了sendMessageAtTime()方法,我們繼續追蹤下去:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
到這裡就算是結束了,它首先獲取當前Looper所關聯的MessageQueue,然後將Message儲存到相應的MessageQueue()中。關於enqueueMessaage()方法參見前篇部落格:MessageQueue工作原理。
Handler傳送訊息已經清楚了,接下來再來看看Handler如何處理訊息。
在使用Handler的時候我們每次都要呼叫(主執行緒除外)Looper.loop()方法,這個方法作用:當MQ中有訊息要執行時呼叫該條Message所屬Handler的dispatchMessage(msg)方法處理訊息(詳細見:Looper工作原理),ok,我們開啟dispatchMessage(msg)原始碼:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
該方法中首先判斷Message是否存在其介面CallBack,如果有就呼叫handleCallback(msg),否則判斷時候Handler介面Callback是否為null,如果不為null就呼叫CallBack.handleMessage(msg)方法,否則呼叫Handler的handleMessage(msg)方法。而CallBack.handleMessage(msg)方法和Handler的handleMessage(msg)方法都需要開發者自己手動處理。