一人一貓旅行記之Handler原理
如果在主執行緒(也叫UI執行緒)中執行一些耗時操作,會出現ANR問題。為了避免ANR,需要將耗時操作,如網路請求啊、資料庫操作啊、讀取檔案等等的操作,開啟一個子執行緒來處理。
在耗時操作執行完畢後,直接在子執行緒中更新UI怎麼樣呢?
一般來說,會出現下面這個錯誤:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6891)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1048)
at android.view.View.requestLayout(View.java:19781)
at android.view.View.requestLayout(View.java:19781)
at android.view.View.requestLayout(View.java:19781)
at android.view.View.requestLayout(View.java:19781)
at android.view.View.requestLayout(View.java:19781)
at android.view.View.requestLayout(View.java:19781)
at androidx.constraintlayout.widget.ConstraintLayout.requestLayout(ConstraintLayout.java:3172)
at android.view.View.requestLayout(View.java:19781)
at android.widget.TextView.checkForRelayout(TextView.java:7368)
at android.widget.TextView.setText(TextView.java:4480)
at android.widget.TextView.setText(TextView.java:4337)
at android.widget.TextView.setText(TextView.java:4312)
at icbc.agree.tmpapppp.MainActivity$2.run(MainActivity.java:37)
從上面的程式碼提示,不難看出UI是隻能在主執行緒中更新的,這也是為什麼主執行緒也被稱為UI執行緒的原因。
但是我們在onCreate中啟動一個子執行緒去更改一個TextView的文字,會驚奇的發現程式竟然可以正常執行,這是怎麼回事呢?
其實是因為,檢查執行緒的工作是ViewRootImpl來完成的,但是在onCreate中啟動子執行緒,可能在ViewRootImpl物件建立之前便已經執行了,TextView的setText方法,不相信的話可以在setText之前sleep一段時間看看哈。
既然不可以在子執行緒更新UI,那麼我們需要一個機制來通知主執行緒進行UI更新,這便是Handler的職責!
Handler是如何實現的呢?
要想搞明白Handler的原理,我們需要認識幾個概念:
Message:Handler傳遞和處理的資料的載體
Message Queue:訊息佇列
Looper:一個不斷從訊息佇列中取出資料的迭代器
Handler的工作流程大致如上圖所示,Handler通過sendMessage等方法,傳送一個Message,將其放到MessageQueue中,Looper不斷的取出佇列中的資料,並進行分發處理。
接下來,從安卓系統原始碼再來看一下Handler的工作原理:
首先,我們看一下主執行緒中的工作
public static void main(String[] args) {
<-省略部分不相關程式碼->
Looper.prepareMainLooper();
<-省略部分不相關程式碼->
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
我們看到Looper.prepareMainLooper();這行程式碼,可以到Looper中看一下它的作用,其實最終是呼叫的prepare方法,而它的作用就是建立一個訊息佇列,程式碼如下:
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
在主執行緒中的最後是呼叫的loop(),現在我們看一下對應的程式碼:
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
<-省略程式碼部分->
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
<-省略程式碼部分->
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
<-省略程式碼部分->
}
}
我們分析下上面這段程式碼,我們獲取到Message Queue,然後開啟一個無限迴圈,通過queue.next()取出來Message,如果訊息為空,說明訊息佇列中沒有訊息了,所以停止迴圈
最後的msg.target.dispatchMessage(msg);應該很熟悉了吧?這個target就是傳送訊息的Handler。
現在我們再去看一下,Handler的部分:
無論是sendMessage還是sendEmptyMessage,最終都是呼叫的sendMessageAtTime,所以我們直接看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);
}
繼續往下看
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
找到target了吧?有興趣的可以繼續往下找找程式碼,其實就是將Message加到訊息佇列中
好的,至此Handler完整的流程便梳理完了!
相關文章
- 一人一貓旅行記之Intent傳遞資料原理Intent
- Android Handler原理Android
- Handler原理分析
- Android Handler機制之記憶體洩漏Android記憶體
- Handler全家桶之 —— Handler 原始碼解析原始碼
- Android Handler機制之Handler 、MessageQueue 、LooperAndroidOOP
- 講講Handler實現原理
- [- Video篇 -]:記一次Handler的使用IDE
- swupdate 之 readback handler
- Android Handler與Looper原理簡析AndroidOOP
- Handler怎麼進行執行緒通訊?Handler原理解讀執行緒
- Android 基礎之 HandlerAndroid
- 大學生程式設計第一人,一人抵一城!程式設計
- Android 進階 ———— Handler系列之建立子執行緒HandlerAndroid執行緒
- 少女與貓的時間旅行,《Timelie》5.21 Steam正式發售
- Android Handler機制之ThreadLocalAndroidthread
- Android學習筆記·HandlerAndroid筆記
- Android Handler MessageQueue Looper 訊息機制原理AndroidOOP
- Android原始碼學習之handlerAndroid原始碼
- netty系列之:Event、Handler和PipelineNetty
- 2018.03.06 Android Handler學習筆記Android筆記
- Android-Handler訊息機制實現原理Android
- 一人一貸P2P
- 《神之天平》:2022年最正JRPG 一人成軍“老頭環”
- 養貓日記-20201205
- Android Handler機制之總目錄Android
- mysql handler語句之一MySql
- 《王者榮耀》新模式“契約之戰”來了:一人操作倆英雄模式
- Python 日誌列印之自定義logger handlerPython
- MySQL Cases-記錄大量waiting for handler commitMySqlAIMIT
- Handler的一點理論分析
- 記一次 .NET 某旅行社Web站 CPU爆高分析Web
- 從Handler.post(Runnable r)再一次梳理Android的訊息機制(以及handler的記憶體洩露)Android記憶體洩露
- 旅行青蛙(旅かえる)逆向筆記筆記
- Android Handler訊息傳遞機制:圖文解析工作原理Android
- 一人兩年前端的自我總結前端
- Unity第一人稱鏡頭模板Unity
- Android 之 “只是想來談談 Handler 機制”Android