每一個初學Android開發的都繞不開Handler這個「坎」,爲何說是個坎呢,首先這是Android架構的精髓之一,其次大部分人都是知其然殊不知其因此然。今天看到Handler.post這個方法以後決定再去翻翻源代碼梳理一下Handler的實現機制。java
先來一個必背口訣「主線程不作耗時操做,子線程不更新UI」,這個規定應該是初學必知的,那要怎麼來解決口訣裏的問題呢,這時候Handler就出如今咱們面前了(AsyncTask也行,不過本質上仍是對Handler的封裝),來一段經典經常使用代碼(這裏忽略內存泄露問題,咱們後面再說):bash
首先在Activity中新建一個handler:網絡
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 0:
mTestTV.setText("This is handleMessage");//更新UI
break;
}
}
};
複製代碼
而後在子線程裏發送消息:架構
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);//在子線程有一段耗時操做,好比請求網絡
mHandler.sendEmptyMessage(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
複製代碼
至此完成了在子線程的耗時操做完成後在主線程異步更新UI,但是並無用上標題的post,咱們再來看post的版本:異步
private Handler mHandler;//全局變量
@Override
protected void onCreate(Bundle savedInstanceState) {
.......
mHandler = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);//在子線程有一段耗時操做,好比請求網絡
mHandler.post(new Runnable() {
@Override
public void run() {
mTestTV.setText("This is post");//更新UI
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
複製代碼
從表面上來看,給post方法傳了個Runnable,像是開了個子線程,但是在子線程裏並不能更新UI啊,那麼問題來了,這是怎麼個狀況呢?帶着這個疑惑,來翻翻Handler的源碼:async
先來看看普通的sendEmptyMessage是什麼樣子:ide
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
複製代碼
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
複製代碼
將咱們傳入的參數封裝成了一個消息,而後調用sendMessageDelayed:oop
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
複製代碼
再調用sendMessageAtTime:post
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);
}
複製代碼
好了,咱們再來看post():ui
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);//getPostMessage方法是兩種發送消息的不一樣之處
}
複製代碼
方法只有一句,內部實現和普通的sendMessage是同樣的,可是隻有一點不一樣,那就是 getPostMessage(r) 這個方法:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
複製代碼
這個方法咱們發現也是將咱們傳入的參數封裝成了一個消息,只是此次是m.callback = r,剛纔是msg.what=what,至於Message的這些屬性就不看了
看到這裏,咱們只是知道了post和sendMessage原理都是封裝成Message,可是仍是不清楚Handler的整個機制是什麼樣子,繼續探究下去。
剛纔看到那兩個方法到最終都調用了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);
}
複製代碼
這個方法又調用了 enqueueMessage,看名字應該是把消息加入隊列的意思,點進去看下:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
複製代碼
mAsynchronous這個異步有關的先無論,繼續將參數傳給了queue的enqueueMessage方法,至於那個msg的target的賦值咱們後面再看,如今繼續進入MessageQueue類的enqueueMessage方法,方法較長,咱們看看關鍵的幾行:
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
複製代碼
果真像方法名說的同樣,一個無限循環將消息加入到消息隊列中(鏈表的形式),可是有放就有拿,這個消息怎樣把它取出來呢?
翻看MessageQueue的方法,咱們找到了next(),代碼太長,不贅述,咱們知道它是用來把消息取出來的就好了。不過這個方法是在什麼地方調用的呢,不是在Handler中,咱們找到了Looper這個關鍵人物,我叫他環形使者,專門負責從消息隊列中拿消息,關鍵代碼以下:
for (;;) {
Message msg = queue.next(); // might block
...
msg.target.dispatchMessage(msg);
...
msg.recycleUnchecked();
}
複製代碼
簡單明瞭,咱們看到了咱們剛纔說的msg.target,剛纔在Handler中賦值了msg.target=this,因此咱們來看Handler中的dispatchMessage:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
複製代碼
msg的callback應該已經想到是什麼了,就是咱們經過Handler.post(Runnable r)傳入的Runnable的run方法,這裏就要提提java基礎了,直接調用線程的run方法至關因而在一個普通的類調用方法,仍是在當前線程執行,並不會開啓新的線程。
因此到了這裏,咱們解決了開始的疑惑,爲何在post中傳了個Runnable仍是在主線程中能夠更新UI。
繼續看如果msg.callback爲空的狀況下的mCallback,這個要看看構造方法:
1.
public Handler() {
this(null, false);
}
2.
public Handler(Callback callback) {
this(callback, false);
}
3.
public Handler(Looper looper) {
this(looper, null, false);
}
4.
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
5.
public Handler(boolean async) {
this(null, async);
}
6.
public Handler(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 that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
7.
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
複製代碼
具體的實現就只有最後兩個,已經知道mCallback是怎麼來的了,在構造方法中傳入就行。
最後若是這兩個回調都爲空的話就執行Handler自身的handleMessage(msg)方法,也就是咱們熟知的新建Handler重寫的那個handleMessage方法。
看到了這裏有一個疑惑,那就是咱們在新建Handler的時候並無傳入任何參數,也沒有哪裏顯示調用了Looper有關方法,那Looper的建立以及方法調用在哪裏呢?其實這些東西Android自己已經幫咱們作了,在程序入口ActivityThread的main方法裏面咱們能夠找到:
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
Looper.loop();
...
複製代碼
已經大概梳理了一下Handler的消息機制,以及post方法和咱們經常使用的sendMessage方法的區別。來總結一下,主要涉及四個類Handler、Message、MessageQueue、Looper:
新建Handler,經過sendMessage或者post發送消息,Handler調用sendMessageAtTime將Message交給MessageQueue
MessageQueue.enqueueMessage方法將Message以鏈表的形式放入隊列中
Looper的loop方法循環調用MessageQueue.next()取出消息,而且調用Handler的dispatchMessage來處理消息
在dispatchMessage中,分別判斷msg.callback、mCallback也就是post方法或者構造方法傳入的不爲空就執行他們的回調,若是都爲空就執行咱們最經常使用重寫的handleMessage。
再來看看咱們的新建Handler的代碼:
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
...
}
};
複製代碼
當使用內部類(包括匿名類)來建立Handler的時候,Handler對象會隱式地持有Activity的引用。
而Handler一般會伴隨着一個耗時的後臺線程一塊兒出現,這個後臺線程在任務執行完畢後發送消息去更新UI。然而,若是用戶在網絡請求過程當中關閉了Activity,正常狀況下,Activity再也不被使用,它就有可能在GC檢查時被回收掉,但因爲這時線程還沒有執行完,而該線程持有Handler的引用(否則它怎麼發消息給Handler?),這個Handler又持有Activity的引用,就致使該Activity沒法被回收(即內存泄露),直到網絡請求結束。
另外,若是執行了Handler的postDelayed()方法,那麼在設定的delay到達以前,會有一條MessageQueue -> Message -> Handler -> Activity的鏈,致使你的Activity被持有引用而沒法被回收。
解決方法之一,使用弱引用:
static class MyHandler extends Handler {
WeakReference<Activity > mActivityReference;
MyHandler(Activity activity) {
mActivityReference= new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
final Activity activity = mActivityReference.get();
if (activity != null) {
mImageView.setImageBitmap(mBitmap);
}
}
}
複製代碼