深入瞭解android中的訊息機制Handler
什麼是Handler?
handler是Android給我們提供用來更新UI的一套機制,也是一套訊息處理機制.
我們可以使用它發送訊息,也可以通過它處理訊息.
我們為什麼要使用Handler?
Android在設計的時候,就封裝了一套訊息建立,傳遞,處理機制,如果不遵循這樣的機制,就沒有辦法更新UI,而且還會拋出異常資訊.
例如:大家都知道,更新UI的操作一般都是放在main線程中,當我們需要在子線程中更新UI時,就需要使用到了Handler,雖然在子線程更新Ui的方法有好幾種,但內部實現原理基本都是通過Handler發送訊息處理的,不要著急,下面會提到.
Handler的使用:
sendMessage()方法的使用:
package com.hnthgys.mytext;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.widget.TextView;public class MainActivity extends AppCompatActivity { private TextView tv; //建立main線程的Handler private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); tv.setText("我是通過handler發送訊息更新的"); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (TextView) findViewById(R.id.textview); //開啟子線程 new Thread(){ @Override public void run() { super.run(); //例如此處我們正在執行一個耗時操作,執行完畢後發送訊息更新ui //發送一個空訊息 handler.sendEmptyMessage(0); //如果此處我們需要使用執行完耗時操作的資料,可以這樣寫 //Message msg = handler.obtainMessage(); //msg.obj = "資料"; //handler.sendMessage(msg); } }.start(); }}
sendEmptyMessage(); 此方法和sendMessage使用一致,區別就是發送一個空訊息.
sendMessageDelayed(); 發送一個延時執行的訊息
post(Runnable);該方法可以在子線程中更新UI,該方法運行在main線程中
removeCallbacksAndMessages();移除回調和訊息;例如:我們在使Handler輪播一些圖片時,想讓它停止輪播,就可以使用這個方法.
android為什麼要設計只能通過handler來更新UI呢?
最根本的的目的就是解決多線程並發問題.假設如果在一個activity當中,有多個線程去更新UI,並且都沒有加鎖機制,那麼會產生什麼樣子的問題呢? 更新介面錯誤.
你可能會說,我可以使用加鎖的多線程啊,如果對更新UI的操作都進行加鎖處理的話,應用程式的效能會大大下降.
處於對以上問題的考慮,Android給我們提供了一套更新UI的機制,我們只需要遵循這樣的機制就可以了.
根本不用關心多線程的問題,所以更新UI的操作,都是在主線程的訊息佇列當中去輪詢處理的.
Handler的原理是什麼呢?
一,Handler封裝了訊息的發送,(主要包括訊息發送給誰)
Looper(Handler內部自己的Looper)
1,內部包含一個訊息佇列,也就是MessageQueue,所有的Handler發送訊息
都走向這個訊息佇列.
2,Looper.loop()方法,就是一個死迴圈,不斷的從MessageQueue中取訊息,
如果有訊息就處理訊息,沒有訊息就阻塞.
二,MessageQueue,就是一個訊息佇列,可以添加訊息,並處理訊息.
三,Handler,內部會跟Lopper進行關聯,也就是說在Handler的
內部可以找到Looper,找到了Lopper也就找到 了MessageQueue,
在Handler中發送訊息,其實就是向MessageQueue隊列中發送訊息.
Handler原理總結:
Handler負責發送訊息,Loooper負責接收Handler發送的訊息,
並直接把訊息回傳給Handler自己.
MessageQueue就是一個儲存訊息的容器.
Handler使用中遇到的問題:
在非UI線程中更新UI,拋出的異常:
在子線程建立Handler,拋出的異常:
注意:當需要在子線程中建立Handler時,需要先建立一個Looper,因為子線程中沒有Looper對象
HandlerThread又是什麼?
當我們向建立一個與線程相關的Handler時,我們可以使用HandlerThread,來解決多線程的並發問題.
子線程與主線程如何互發訊息:
主線程Handler向子線程發送訊息(虛擬碼)
子線程Handler向主線程發送訊息(虛擬碼)
Android在子線程中更新UI的幾種方式:
使用圖片吧,以前做的筆記,看著感覺更加清晰..
非UI線程真的不能更新UI嗎?
答案是能,對.你沒有看錯,非UI線程也能更新UI.可能你會覺得我在扯淡,下面看一段代碼:
package com.hnthgys.mytext;import android.os.Bundle;import android.os.Handler;import android.support.v7.app.AppCompatActivity;import android.widget.TextView;public class MainActivity extends AppCompatActivity { private TextView tv; //建立main線程的Handler private Handler handler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (TextView) findViewById(R.id.textview); //開啟子線程 new Thread(){ @Override public void run() { super.run(); //SystemClock.sleep(100); tv.setText("我是在子線程中更新的UI"); } }.start(); }}
我把布局代碼也貼出來,
執行:
可能看到這,你已經目瞪口呆了,這怎麼可能,fuck,這完全顛覆了啊…..
主要原因:
當我們在更新UI時,Android中的ViewRootImpl類中的checkThread()方法會檢查當前更新UI所在的線程,
3937 void More ...checkThread() { //檢查執行更新UI所在的線程3938 if (mThread != Thread.currentThread()) { //如果不在UI線程,就會拋出下面的異常,大家應該很眼熟吧3939 throw new CalledFromWrongThreadException(3940 "Only the original thread that created a view hierarchy can touch its views.");3941 }3942 }
查看系統源碼後,你會發現,ViewRootImpl類會在 Activity的onResume()方法執行完成後才初始化,這也就解釋了上面代碼能啟動並執行原因了,但是,你發現沒有,我們在子線程中沒有做任何的耗時操作,如果我在子線程中添加這句代碼:
SystemClock.sleep(100);
那麼系統將會拋出異常:”Only the original thread that created a view hierarchy can touch its views.”
不能在非UI線程中更新UI.
那麼問題來了,如果ViewRootImpl類沒有初始化完成,那麼view視圖是如何顯示出來的呢???我也正在解決中…….
另外,當我們在子線程中擷取到ViewRoot,我們可以調用addView()方法在子線程中更新UI,這其中的詳情就靠大家去探索了…..