01、Android--Handler原理解析
Handler原理
Handler是支撐整個Android系統執行的基礎,本質上Android系統都是由事件驅動的。而處理事件的核心就在於Handler。
關於Handler、Looper、MessageQueue和Message的概述如下:
Handler 訊息機制中作為一個對外暴露的工具,其內部包含了一個 Looper 。負責Message的傳送及處理。
Looper 作為訊息迴圈的核心,其內部包含了一個訊息佇列 MessageQueue,用於記錄所有待處理的訊息。(執行緒切換在這裡完成)
MessageQueue 則作為一個訊息佇列,則包含了一系列連結在一起的Message(內部是單鏈表)
Message 則是訊息體,內部又包含了一個目標處理器target,這個target正是最終處理它的Handler
Handler的使用
Handler的使用非常簡單,一般分為在主執行緒建立Handler和在子執行緒建立Handler兩種方式。
一般是在主執行緒中實現一個Handler,然後在子執行緒中使用它。
public class MainActivity extends AppCompatActivity { private Handler handler = new MyHandler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new Thread(){ @Override public void run() { handler.sendEmptyMessageDelayed(1, 1000); } }; } // 自定義一個Handler class MyHandler extends Handler { @Override public void handleMessage(@NonNull Message msg) { Log.i("MainActivity", "主執行緒:handleMessage:" + msg.what); } } }
或者有時候需要在子執行緒中建立執行在主執行緒中的Handler
public class MainActivity extends AppCompatActivity { private Handler handler = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new Thread(){ @Override public void run() { Looper.prepare(); handler = new MyHandler(); handler.sendEmptyMessageDelayed(1, 1000); Looper.loop(); } }; } // 自定義一個Handler class MyHandler extends Handler { @Override public void handleMessage(@NonNull Message msg) { Log.i("MainActivity", "主執行緒:handleMessage:" + msg.what); } } }
Handler
Handler可以用來發送訊息,我們這裡從sendMessage()方法開始,原始碼如下所示:
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
呼叫了sendMessageDelayed方法:
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
繼而呼叫sendMessagAtTime方法:
public boolean sendMessageAtTime(@NonNull 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);
}
在這裡賦值MessageQueue並且將它作為引數傳遞給enqueueMessage()方法,其方法的呼叫順序如下:
可以看到無論如何,最後都會走到enqueueMessage()方法中。接下來看看enqueueMessage()方法:
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
enqueueMessage()方法一共做了兩件事情,一個是給Message賦值,一個是呼叫傳進來的這個MessageQueue的enqueueMessage()方法。
target表示的就是當前的Handler,也就是說每個發出去的Message都持有把它發出去的Handler的引用。
MessageQueue
Handler這個mQueue就是上文我們提到過的MessageQueue物件,它是一個單鏈表(一種鏈式存取的資料結構)。
我們在sendMessageAtTime()方法中對MessageQueue進行賦值,並且當做引數傳遞給Handler的enqueueMessage()方法。
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
// 對MessageQueue進行賦值,mQueue來源於構造方法
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);
}
其實MessageQueue是在構造方法中進行賦值的,接下來我們看看構造方法的原始碼:
public Handler(@Nullable Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
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());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
// 對MessageQueue進行賦值,來自於Looper
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
這裡我們可以看到,MessageQueue的例項建立應該在Looper中,這裡先不研究Looper,繼續探索MessageQueue。
最後傳送訊息都呼叫的是MessageQueue的queue.enqueueMessage(msg, uptimeMillis)方法。
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
// 拿到佇列頭部
Message p = mMessages;
boolean needWake;
// 如果訊息不需要延時或訊息的執行時間比頭部早,插到佇列頭部
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// 訊息插到佇列中間
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p;
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
主要分為三個步驟,可以參看程式碼標註:
mMessages 是佇列的第一訊息,獲取到它判斷訊息佇列是不是空的,是則將當前的訊息放到佇列頭部。
如果當前訊息不需要延時,或當前訊息的執行時間比頭部訊息早,也是放到佇列頭部。
如果不是以上情況,說明當前佇列不為空,並且佇列的頭部訊息執行時間比當前訊息早,需要將它插入到佇列的中間位置。
我們也許會產生疑問,如何判斷這個位置呢?其實依舊是通過訊息被執行的時間。
通過遍歷整個佇列,當佇列中的某個訊息的執行時間比當前訊息晚時,將訊息插到這個訊息的前面。
Looper
我們再來看看Handler的構造方法,如下所示:
public Handler(@Nullable Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
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的例項
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
// 對MessageQueue進行賦值,來自於Looper
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
可以看到在這裡通過mLooper = Looper.myLooper()程式碼賦值了Looper的例項,它作為訊息迴圈的核心,不斷的從MessageQueue中取出訊息進行分發。
我們再來看看Lopper的myLooper()方法,原始碼如下:
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
可以看到這個sThreadLocal是一個ThreadLocal類,並且它的泛型是Looper物件。
ThreadLocal提供了執行緒的區域性變數,每個執行緒都可以通過set()和get()來對這個區域性變數進行操作,但不會和其他執行緒的區域性變數進行衝突,實現了執行緒的資料隔離。
我們看到原始碼中有個註釋:除非您已呼叫prepare(),否則sThreadLocal.get()將返回null。接下來看看prepare()方法:
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
// 如果sThreadLocal有值,就拋異常,沒有值才會塞進去一個值。
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
prepare方法必須呼叫但也只能呼叫一次,不呼叫沒有值,拋異常,呼叫多次也還拋異常。
在構造Handler之前,必須呼叫Looper的prepare()方法建立Looper。
接下來看看sThreadLocal.set(new Looper(quitAllowed))方法的原始碼:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
set方法首先獲取到了當前的執行緒,然後獲取一個map。這個map是以鍵值對形式儲存內容的。
如果不為空就塞進去值,如果獲取的map為空,就建立一個map。這裡面的key是當前的執行緒,這裡面的value就是Looper。(執行緒繫結)
接下來我們去看看Lopper的構造方法,原始碼如下所示:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看到在Looper的構造方法中建立了MessageQueue物件。
由於prepare()方法只能呼叫一次,所以只會建立一個Looper物件,所以:
一個執行緒中只會建立一個Looper物件,而一個Looper物件也只會建立一個MessageQueue物件。
最後,主執行緒建立Handler沒有呼叫prepare()方法,那麼Looper是從哪裡來的?
其實主執行緒的Looper是在ActivityThread類中main()方法中建立的,接下來我們看看這個main()方法:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
Process.setArgV0("<pre-initialized>");
// 就是這裡呼叫了prepare方法
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
// 就是這裡給主執行緒建立了Looper
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
接下來我們看看Looper的prepareMainLooper方法:
public static void prepareMainLooper() {
//設定不允許退出的Looper
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
prepare(false)建立了Looper並且把它和當前執行緒一起塞進map中的。
Looper要分發訊息並不知道具體傳送訊息的時間,只能開啟死迴圈不斷從MessageQueue中獲取訊息,所以Looper.loop()方法就是開啟了死迴圈:
public static void loop() {
// 拿到當前執行緒的Looper
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// ......
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// ......
msg.recycleUnchecked();
}
}
在迴圈中Looper不停的取出訊息,拿到Message物件以後,會去呼叫Message的target欄位的dispatchMessage方法,其中target就是傳送訊息的Handler。
可以再來看看dispatchMessage方法,原始碼如下所示:
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
可以看到在回撥中呼叫了handleMessage(msg)方法。
通過Handler傳送訊息時,訊息會回到Handler初始化的執行緒,而不一定是主執行緒,所以Handler其實是藉助共享變數來進行執行緒切換的。
Looper和ANR
其實整個Android就是在一個Looper的loop迴圈的,整個Android的一切都是以Handler機制進行的,即只要有程式碼執行都是通過Handler來執行的,而所謂ANR便是Looper.loop()沒有得到及時處理,一旦沒有訊息,Linux的epoll機制則會通過管道寫檔案描述符的方式來對主執行緒進行喚醒與沉睡,Android裡呼叫了linux層的程式碼實現在適當時會睡眠主執行緒