Android之Handler
一、基礎知識:
- Android 的執行緒分兩種:主執行緒(又稱:UI執行緒)和子執行緒(又稱工作執行緒,即普通的new Thread)。
- 主執行緒由應用程式啟動時自動建立,使用者使用app進行介面互動、獲取操作結果,就是基於UI執行緒。如:activity跳轉、設定TextView文字、showToast等。而子執行緒則通過new Thread建立。
- UI執行緒執行程式碼效率越高,介面響應越快,使用者感覺就越流暢。為了更好的使用者體驗,Android系統中的耗時操作(包括耗效能、複雜數學運算、大檔案下載等)我們一般都在子執行緒中執行,從而避免影響使用者點選或者佈局渲染等處理。在Android4.4版本之後,如果直接在主執行緒進行網路請求,系統會丟擲NetWrokOnMainThreadException異常,以此要求我們使用子執行緒處理。主執行緒應做到專執行緒專用:只處理UI相關操作,否則程式會報ANR錯誤。
- 同步和非同步的區別:
- 同步:在UI執行緒下載圖片,下載成功後,在UI執行緒更新圖片
- 非同步:在UI執行緒new一個子執行緒,在子執行緒執行下載操作,成功後,子執行緒發通知告訴UI執行緒,UI執行緒顯示下載圖片。
上面提到:子執行緒作用是執行比較耗時的操作,如聯網下載資料等,但Android子執行緒是不安全的,只能在主執行緒中更新UI。相信聰明的你看到這裡,一定能夠體會“非同步”的作用了!那“子執行緒發通知告訴UI執行緒”是怎麼實現呢?這就要提到handler了。
二、Handler的作用:
在子執行緒傳送訊息(Message),通知建立它的執行緒(通常是UI執行緒)處理資料結果。
三、如何使用Handler:
public class MainActivity extends Activity {
private static final int MAKE_TOAST = 0x01;
// handler建構函式為空,等同於new Handler(getMainLooper()),與Activity所在的UI執行緒關聯
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case MAKE_TOAST:
Toast.makeText(MainActivity.this, "hello", Toast.LENGTH_SHORT).show();
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
HttpUtils.doGet("https://www.baidu.com/", new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
mHandler.sendEmptyMessageDelayed(MAKE_TOAST,200); // handler傳送訊息
}
});
}
}
四、開發者容易犯的錯誤:
獲取資料後更新UI的問題,比如:在onClick事件(執行在UI執行緒)中,執行以下程式碼(doGet方法執行了new Thread,建立了子執行緒),在獲取網路資訊後,需要彈出一個Toast
HttpUtils.doGet("https://www.baidu.com/", new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Toast.makeText(OkHttpActivity.this,"",Toast.LENGTH_SHORT).show();
}
});
/*例子2,執行時報java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()異常*/
new Thread(new Runnable(){
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
i++;
}
Handler handler=new Handler(); //Handler放到當前新執行緒中
handler.sendEmptyMessage(3);
}
}).start();
直接在在新的執行緒中建立Handler和傳送訊息,這是不允許的
這是因為Android規定了只允許UI執行緒修改Activity裡的UI元件,而我們剛才的操作在子執行緒中修改Activity裡的UI元件,才會導致UI操作的執行緒不安全,並報出錯誤。
但細心的朋友會問:前面 “三、Handler常見用法” 示例中,為什麼不會報錯,在子執行緒用就出錯了?
奧妙在於:
Android系統會自動為主執行緒建立Looper物件,開啟訊息迴圈,
但子執行緒預設沒有開啟訊息迴圈。
如果子執行緒要使用handler,應該手動開啟訊息迴圈。
Looper物件通過MessageQueue來存放訊息和事件。
一個執行緒只能有一個Looper,對應一個MessageQueue。
然後通過Looper.loop() 讓Looper開始工作,從訊息佇列裡取訊息,處理訊息。
注意:寫在Looper.loop()之後的程式碼不會被執行,這個函式內部應該是一個迴圈,當呼叫mHandler.getLooper().quit()後,loop才會中止,其後的程式碼才能得以執行。
五、Handler工作原理
一)Android訊息機制的相關概念
- 主執行緒(UI執行緒)
定義:當程式第一次啟動時,Android會同時啟動一條主執行緒(Main Thread)
作用:主執行緒主要負責處理與UI相關的事件
- Message(訊息)
定義:Handler接收和處理的訊息物件(Bean物件)
作用:通訊時相關資訊的存放和傳遞
- ThreadLocal
定義:執行緒內部的資料儲存類
作用:負責儲存和獲取本執行緒的Looper
- Message Queue(訊息佇列)
定義:採用單連結串列的資料結構來儲存訊息列表
作用:用來存放通過Handler發過來的Message,按照先進先出執行
- Handler(處理者)
定義:Message的主要處理者
作用:負責傳送Message到訊息佇列&處理Looper分派過來的Message
- Looper(迴圈器)
定義:扮演Message Queue和Handler之間橋樑的角色
作用:
訊息迴圈:迴圈取出Message Queue的Message
訊息派發:將取出的Message交付給相應的Handler
二)圖片解讀它們之間的關係
三)文字解讀它們之間的關係
Looper中存放有MessageQueen,MessageQueen中又有很多Message,當我們的Handler傳送訊息的時候,會獲取當前的Looper,並在當前的Looper的MessageQueen當中存放我們傳送的訊息,而我們的MessageQueen也會在Looper的帶動下,一直迴圈的讀取Message資訊,並將Message資訊傳送給Handler,並執行HandlerMessage()方法
其實這是一個迴圈的過程,讀懂這句話和看懂圖解很重要,會給我們下面的原始碼分析帶來很大的幫助,所以建議大家先讀懂前面的內容。
Android訊息機制的通訊流程
原始碼分析請參考後面“Looper原始碼分析”。
六、子執行緒之間使用Handler
package lib.com.myapplication;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
// 注意:這兩個雖然屬於Activity變數,但它們是在子執行緒建立的,
// 建立時呼叫new Handler()建構函式取“所線上程的looper”,
// 即等效於 new Hanlder(子執行緒.looper)
private Handler handler1 ;
private Handler handler2 ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new MyThread1().start();
new MyThread2().start();
}
class MyThread1 extends Thread {
@Override
public void run() {
super.run();
// 為當前執行緒建立looper
Looper.prepare();
// 關聯Thread1 的 looper
handler1 = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
System.out.println( "threadName--" + Thread.currentThread().getName() + "messageWhat-"+ msg.what );
}
};
try {
sleep( 3000 );
} catch (InterruptedException e) {
e.printStackTrace();
}
handler2.sendEmptyMessage( 2 ) ; // 傳送訊息給子執行緒2
Looper.loop(); // 讓Looper迴圈取訊息,傳送給handler,讓handler處理
}
}
class MyThread2 extends Thread {
@Override
public void run() {
super.run();
Looper.prepare();
// 關聯Thread2 的 looper
handler2 = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
System.out.println( "threadName--" + Thread.currentThread().getName() + "messageWhat-"+ msg.what );
}
};
try {
sleep( 4000 );
} catch (InterruptedException e) {
e.printStackTrace();
}
handler1.sendEmptyMessage( 5 ) ;
Looper.loop();
}
}
}
注意:
1、呼叫Looper類的 prepare()
方法可以為當前執行緒建立一個訊息迴圈,呼叫loop()
方法使之處理資訊,直到迴圈結束。
2、new Handler時,有幾個建構函式可選,如果不選擇Looper類物件引數,會獲取當前執行緒的Looper物件,即將當前執行緒的訊息迴圈作為Handler關聯的訊息迴圈。
3、訊息處理機制中,訊息存放在一個訊息佇列中,而執行緒圍繞這個佇列進入一個無限迴圈,直到程式退出。如果佇列中有訊息,執行緒就會把訊息取出來,並分發給相應的Handler進行處理;如果佇列中沒有訊息,執行緒就會進入空閒等待狀態,等待下一個訊息的到來。
七、HandlerThread介紹
顧名思義,Handler---->Thread,handler是修飾thread的,說明它是一個Thread執行緒類。
HandlerThread是一個內建 Looper 的Thread,讓我們可以直接線上程中使用 Handler 來處理非同步任務。
- HandlerThread將loop轉到子執行緒中處理,說白了就是將分擔MainLooper的工作量,降低了主執行緒的壓力,使主介面更流暢。
- 開啟一個執行緒起到多個執行緒的作用。處理任務是序列執行,按訊息傳送順序進行處理。
相比多次使用new Thread(){…}.start()這樣的方式節省系統資源。
但是由於每一個任務都將以佇列的方式逐個被執行到,一旦佇列中有某個任務執行時間過長,那麼就會導致後續的任務都會被延遲處理。 - HandlerThread擁有自己的訊息佇列,它不會干擾或阻塞UI執行緒。
- 通過設定優先順序就可以同步工作順序的執行,而又不影響UI的初始化;
- 比較適用於單執行緒+非同步佇列的場景,比如IO讀寫操作,耗時不多而且也不會產生較大的阻塞。對於網路IO操作,HandlerThread並不適合,因為它只有一個執行緒,還得排隊一個一個等著。
原始碼不多:
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
//注意:指定執行緒優先順序,是android.os.Process.xxx 而不是 java.lang.Thread.xxx 的優先順序!
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
// 子類需要重寫的方法,在這裡做一些執行前的初始化工作
protected void onLooperPrepared() {
}
//獲取當前執行緒的 Looper
//如果執行緒不是正常執行的就返回 null
//如果執行緒啟動後,Looper 還沒建立,就 wait() 等待 建立 Looper 後 notify
public Looper getLooper() {
if (!isAlive()) {
return null;
}
synchronized (this) {
while (isAlive() && mLooper == null) { //迴圈等待
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
//呼叫 start() 後就會執行的 run()
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare(); //幫我們建立了 Looper
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll(); //Looper 已經建立,喚醒阻塞在獲取 Looper 的執行緒
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
// 執行loop內部方法,開始一直迴圈:訊息佇列,取訊息給handler。不會執行下一句
// 注意:迴圈執行的是loop方法內部程式碼,而不是run方法
Looper.loop();
// 當外部執行HandlerThread.quit/quitSafely時,才會執行這句。
mTid = -1;
}
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
public int getThreadId() {
return mTid;
}
}
可以看到,
①HandlerThread 本質還是個 Thread,建立後別忘了呼叫 start()
。
②在 run()
方法中建立了 Looper,呼叫 onLooperPrepared
後開啟了迴圈
③我們要做的就是在子類中重寫 onLooperPrepared
,做一些初始化工作
④在建立 HandlerThread 時可以指定優先順序,注意這裡的引數是 Process.XXX
而不是 Thread.XXX
Process.setThreadPriority(int) A Linux priority level, from -20 for highest scheduling priority to 19 for lowest scheduling priority.
可選的值如下:
public static final int THREAD_PRIORITY_DEFAULT = 0;
public static final int THREAD_PRIORITY_LOWEST = 19;
public static final int THREAD_PRIORITY_BACKGROUND = 10;
public static final int THREAD_PRIORITY_FOREGROUND = -2;
public static final int THREAD_PRIORITY_DISPLAY = -4;
public static final int THREAD_PRIORITY_URGENT_DISPLAY = -8;
public static final int THREAD_PRIORITY_AUDIO = -16;
Looper.quit和Looper.quitSafely區別:
Looper.quit():自API Level 1就存在,它會把MessageQueue訊息池中所有的 延遲 + 非延遲 訊息全部清空,無論是延遲訊息(延遲訊息是指通過sendMessageDelayed或通過postDelayed等方法傳送的需要延遲執行的訊息)還是非延遲訊息。
Looper.quitSafely():在API Level 18後加入,只會清空所有的延遲訊息,並將所有的非延遲訊息派發出去讓Handler去處理.
兩者共同點:只要呼叫,Looper就不再接收新的訊息。這時候再通過Handler呼叫sendMessage或post等方法傳送訊息時均返回false,表示訊息沒有成功放入訊息佇列MessageQueue中,因為訊息佇列已經退出了。
HandlerThread的簡單用法:
//1.初始化,引數為名字,也就是執行緒的名字,後面我們會結合原始碼來看
mHandlerThread = new HandlerThread("WorkThread");
//必須呼叫start方法,因為HandlerThread繼承自Thread來啟動執行緒
mHandlerThread.start();
//初始化Handler,只是傳遞了一個mHandlerThread內部的一個looper
mHandler = new Handler(mHandlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d("WorkThread", (Looper.getMainLooper() == Looper.myLooper()) + "," + msg.what);
}
};
//2.使用
public void send(View view) {
new SendThread(mHandler).start();
}
private final class SendThread extends Thread {
private Handler mHandler;
SendThread(Handler handler) {
this.mHandler = handler;
}
@Override
public void run() {
super.run();
for (int i = 0; i < 3; i++) {
mHandler.sendEmptyMessage(0x1);
SystemClock.sleep(1000);
}
}
}
//3.別忘記釋放,停止Looper訊息迴圈,內部還是呼叫了looper的quit,及時釋放防止記憶體洩漏哦!!
mHandlerThread.quit();
八、防記憶體洩露:This Handler class should be static or leaks might occur
我們在handler物件建立的時候卻會報警告:This Handler class should be static or leaks might occur
public class AutoActivity extends Activity {
// 建立handler時,Looper預設當前UI執行緒關聯
Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_auto);
}
}
意思是:Handler類應該為static型別,否則可能會造成記憶體洩漏。為什麼會造成這種情況呢?
原因:從上文可以知道,handler的作用就是從這個訊息佇列中放入和取出訊息。當我們通過handler將一個Message放入訊息佇列時,這個Message就會持有一個handler物件的引用。
Activity在被結束之後,MessageQueue並不會隨之被結束,如果這個訊息佇列中存在Message,則導致持有handler的引用,但是又由於Activity被結束了,Message無法被處理,從而導致永久持有handler物件,handler永久持有Activity物件,於是發生記憶體洩漏。
為什麼用might occur?
從Activity被結束後,到這個Message被取出來處理之前,這一段時間內(這個might用詞非常準確,一方面訊息可能處理完畢,另一方面延遲未處理才會出現這種情況,若不延遲那麼久,可能性不算高。)這個Message會繼續存活,但是這個Message持有handler的引用,而handler在Activity中建立,會持有Activity的引用,因而當Activity結束後,Activity物件並不能夠被gc回收,因而出現記憶體洩漏。
上面的程式碼可能不夠清晰,再來一段直白點的:
public class SampleActivity extends Activity {
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 延時10分鐘再傳送訊息
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
}
}, 60 * 10 * 1000);
finish(); // 關閉當前Activity
}
}
當Activity被finish()掉,這時activity總該被回收了吧?答案是否定的!因為Message 將存在於訊息佇列中長達10分鐘的時間才會被執行到,這個Message持有一個對Handler的引用,Handler也會持有一個對於外部類(SampleActivity)的隱式引用,這些引用在Message被執行前將一直保持,這樣會保證Activity的上下文不被垃圾回收機制回收,同時也會洩露應用程式的資源(views and resources)。
為什麼為static型別就會解決這個問題呢?
因為在java中所有非靜態的物件都會持有當前類的強引用,而靜態物件則只會持有當前類的弱引用。宣告為靜態後,handler將會持有一個Activity的弱引用,而弱引用會很容易被gc回收,這樣就能解決Activity結束後,gc卻無法回收的情況。
所以解決這個警告就有幾種方法:
一:將hanlder物件宣告為靜態的物件。
二:使用靜態內部類,通過WeakReference實現對Activity的弱引用。具體實現看以下程式碼:
public class AutoActivity extends Activity { MyHandler handler = new MyHandler(this); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_auto); } static class MyHandler extends Handler { WeakReference<AutoActivity> mactivity; public MyHandler(AutoActivity activity){ mactivity = new WeakReference<AutoActivity>(activity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 100: //在這裡面處理msg //通過mactivity.get()獲取Activity的引用(即上下文context) break; default: break; } } } }
順便說下軟引用和弱引用的區別:
- 軟引用(SoftReference):當虛擬機器記憶體不足時,將會回收它指向的物件;需要獲取物件時,可以呼叫get方法
- 弱引用(WeakReference):隨時可能會被垃圾回收器回收,不一定要等到虛擬機器記憶體不足時才強制回收。要獲取物件時,同樣可以呼叫get方法。
二. SoftReference:實現快取機制
例如從網路上獲取圖片,然後將獲取的圖片顯示的同時,通過軟引用快取起來。當下次再去網路上獲取圖片時,首先會檢查要獲取的圖片快取中是否存在,若存在,直接取出來,不需要再去網路上獲取。
另附:
Looper原始碼分析
一、根據上面的例子,為什麼Handler可以在主執行緒中直接可以使用呢?
因為主執行緒(UI執行緒)的Looper在應用程式開啟時建立好了,即在ActivityThread.main方法中建立的,該函式為Android應用程式的入口
public static void main(String[] args) {
...
Process.setArgV0("<pre-initialized>");
//1. 建立訊息迴圈Looper
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...
//2. 執行訊息迴圈
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Looper中最為重要的兩個方法:
- Looper.prepareMainLooper():該方法是Looper物件的初始化
- Looper.loop():該方法會迴圈取出Message Queue的Message,將取出的Message交付給相應的Handler(Looper的作用就體現在這裡)
二、Looper.prepareMainLooper()
//在主執行緒中初始化Looper
public static void prepareMainLooper() {
//在這裡會呼叫prepare(boolean quitAllowed)方法
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
//看下prepare(boolean quitAllowed)方法
private static void prepare(boolean quitAllowed) {
//判斷sThreadLocal是否為null,否則丟擲異常
//即Looper.prepare()方法不能被呼叫兩次
//也就是說,一個執行緒中只能對應一個Looper例項
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//初始化Looper物件設定到ThreadLocal中
sThreadLocal.set(new Looper(quitAllowed));
}
//看下Looper的構造方法
private Looper(boolean quitAllowed) {
//建立了一個MessageQueue(訊息佇列)
//這說明,當建立一個Looper例項時,會自動建立一個與之配對的MessageQueue(訊息佇列)
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
整個Looper的初始化準備工作就完了,這裡做了哪幾件事:
Looper的建立會關聯一個MessageQueen的建立
Looper物件只能被建立一次
Looper物件建立後被存放在sThreadLocal中
三、Looper.loop()
public static void loop() {
//myLooper()方法作用是返回sThreadLocal儲存的Looper例項,如果me為null,loop()則丟擲異常
//也就是說loop方法的執行必須在prepare方法之後執行
//也就是說,訊息迴圈必須要先線上程當中建立Looper例項
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//獲取looper例項中的mQueue(訊息佇列)
final MessageQueue queue = me.mQueue;
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
//進入訊息迴圈
for (;;) {
//next()方法用於取出訊息佇列裡的訊息
//如果取出的訊息為空,則執行緒阻塞
Message msg = queue.next();
if (msg == null) {
return;
}
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
//訊息派發:把訊息派發給msg的target屬性,然後用dispatchMessage方法去處理
//Msg的target其實就是handler物件,下面會繼續分析
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
//釋放訊息佔據的資源
msg.recycleUnchecked();
}
}
整個Looper的迴圈過程就完了,這裡做了哪幾件事:
取出Looper和MessageQueen
進入訊息迴圈,有訊息則分發出去
訊息資源的回收
四、Looper的退出
當然Looper也提供了兩個方法可以退出一個Looper:
quit():quit會直接退出Looper
quitSafety():quitSafety只是設定一個退出標記,然後把訊息佇列中的已有訊息處理完畢後退出Looper
MessageQueen原始碼分析
一、由於MessageQueen是用來存放Message的,那麼是如何儲存Message的呢?
由於Handler使用Post()方法將Message傳遞到MessageQueen中,在MessageQueen中會使用enqueueMessage()方法儲存Message,其實現的方式是通過單連結串列的資料結構來儲存訊息列表
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
...
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;
}
整個進佇列的過程就完了,這裡做了哪幾件事:
首先判斷訊息佇列裡有沒有訊息,沒有的話則將當前插入的訊息作為隊頭,並且這時訊息佇列如果處於等待狀態的話則將其喚醒
若是在中間插入,則根據Message建立的時間進行插入
二、既然MessageQueen存了訊息之後,是如何提供取出來的方法的呢?
我們知道存訊息是Handler存進來的,那麼取訊息就應該是Looper中取了,從Looper的原始碼可以看出,訊息就是在Looper中取出的,其實現是用MessageQueen裡面的next()方法
Message next() {
......
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// nativePollOnce方法在native層,若是nextPollTimeoutMillis為-1,這時候訊息佇列處於等待狀態。
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
//按照我們設定的時間取出訊息
if (msg != null) {
if (now < msg.when) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// 如果訊息佇列中沒有訊息,將nextPollTimeoutMillis設為-1,下次迴圈訊息佇列則處於等待狀態
nextPollTimeoutMillis = -1;
}
//退出訊息佇列,返回null,這時候Looper中的訊息迴圈也會終止。
if (mQuitting) {
dispose();
return null;
}
......
}
.....
}
}
三、在MessageQueen存訊息的媒介當然是通過Message物件啦,那這個Message物件又是什麼呢?
其實這個Message就是用來儲存Message中各種資訊的Bean物件,從原始碼中可以其屬性,這裡例舉我們常用的幾個
public int what;
public int arg1;
public int arg2;
public Object obj;
public Messenger replyTo;
int flags;
long when;
Bundle data;
Handler target;
Runnable callback;
Handler原始碼分析
一、Handler的建立
Handler的建立會關聯一個Looper物件,而Looper物件是關聯著MessageQueen物件,所以在Handler建立時候,取出Looper和MessageQueen
public Handler(Callback callback, boolean async) {
...
//取出Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//取出Looper中的MessageQueen
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
前面我們也說過了Looper是存放在ThreadLocal裡面的,可以看到下面的原始碼就知道了
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
整個建立的過程就完了,這裡做了哪幾件事:
取出Looper
取出Looper中的MessageQueen
二、Handler傳送訊息
1、方式一:sendMessage(Message msg)
//從這裡開始
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);
}
//往下追蹤
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
//往下追蹤
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
//直接獲取MessageQueue
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);
}
//呼叫sendMessage方法其實最後是呼叫了enqueueMessage方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//為msg.target賦值為this,也就是把當前的handler作為msg的target屬性
//如果大家還記得Looper的loop()方法會取出每個msg然後執行msg.target.dispatchMessage(msg)去處理訊息,其實就是派發給相應的Handler
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//最終呼叫queue的enqueueMessage的方法,也就是說handler發出的訊息,最終會儲存到訊息佇列中去
return queue.enqueueMessage(msg, uptimeMillis);
}
2、方式二:post(Ruunable r)
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
其實post()方法最終也會儲存到訊息佇列中去,和上面不同的是它傳進來的一個Runnable物件,執行了getPostMessage()方法,我們往下追蹤
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
實質上就是將這個Runnable儲存在Message的變數中,這就導致了我們下面處理訊息的時候有兩種不同方案
三、Handler處理訊息
你還記得前面所說Looper中msg.target.dispatchMessage()方法嗎?這個方法就是呼叫Handler的dispatchMessage()
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
//1. post()方法的處理方法
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//2. sendMessage()方法的處理方法
handleMessage(msg);
}
}
//1. post()方法的最終處理方法
private static void handleCallback(Message message) {
message.callback.run();
}
//2. sendMessage()方法的最終處理方法
public void handleMessage(Message msg) {
}
整個處理的過程就完了,這裡做了哪幾件事:
post()方法的處理方法就是將傳進來的Runnable執行run()方法
sendMessage()方法的處理方法就是執行handleMessage()空方法,這也是我們為什麼要在Handler重寫這個方法的原因
參考:https://blog.csdn.net/qq_30379689/article/details/53394061
相關文章
- Android Handler機制之Handler 、MessageQueue 、LooperAndroidOOP
- Android 基礎之 HandlerAndroid
- Android開發之HandlerAndroid
- Android 進階 ———— Handler系列之建立子執行緒HandlerAndroid執行緒
- Android原始碼學習之handlerAndroid原始碼
- Android Handler機制之ThreadLocalAndroidthread
- Android Handler機制之總目錄Android
- [Handler]android-Handler解釋Android
- 深入探索Android訊息機制之HandlerAndroid
- Android中HandlerAndroid
- Android Handler原理Android
- Android 之 “只是想來談談 Handler 機制”Android
- Android Handler機制之記憶體洩漏Android記憶體
- Handler全家桶之 —— Handler 原始碼解析原始碼
- Android學習-HandlerAndroid
- Android中的handlerAndroid
- Android之Handler訊息傳遞機制詳解Android
- Android解惑之Handler為什麼需要是static的Android
- Android 之 Looper、MessageQueue、Handler 與訊息迴圈AndroidOOP
- Android訊息傳遞之Handler訊息機制Android
- Android Handler機制之Message的傳送與取出Android
- Android Handler 原始碼解析Android原始碼
- Android Handler 原始碼探索Android原始碼
- Android Handler面試總結Android面試
- Android——Handler原始碼分析Android原始碼
- Android Handler機制理解Android
- Android Handler原理詳解Android
- Android中Handler的使用Android
- Android 面試(五):探索 Android 的 HandlerAndroid面試
- Android Handler機制之Message及Message回收機制Android
- Android Handler機制之迴圈訊息佇列的退出Android佇列
- Android全面解析之由淺及深Handler訊息機制Android
- android訊息機制—HandlerAndroid
- Android學習筆記·HandlerAndroid筆記
- Android訊息機制HandlerAndroid
- Android 8.1 Handler 原始碼解析Android原始碼
- Android Handler機制詳解Android
- Android:Handler學習總結Android