Android中為什么要采用Handler機制?Handler的作用是什么?
Android中的UI線程(主線程)是不安全的,一般來說在子線程中進行UI操作會導致UI線程的阻塞,所以Android提供一套Handler機制來實現異步消息處理。Handler機制的作用是:解決多線程并發的問題(協同其他線程工作),接收其他線程的消息并通過接收到的消息更新主UI線程的內容。
Handler機制是由那些部分構成?各個部分有什么作用?
Handler機制由MessageQueue、Looper以及Handler組成。各部分的功能如下:
MessageQueue的作用是依照隊列先進先出的規則存儲由其他線程的Handler發送過來的Message對象,然后Looper會輪詢并獲取這些Message對象。
Looper的作用是去輪詢MessageQueue,當發現MessageQueue中存在Message,Looper就會將該Message傳遞給對應的Handler的handleMessage( )方法進行處理。(Looper就像是一個消息泵,把得到的Message泵給對應的Handler對象)
Handler的作用是發送消息給MessageQueue以及處理從Looper傳遞過來的Message。
子線程要實現Handler機制處理消息跟UI線程之間有什么不同?
UI線程中的MessageQueue以及Looper是默認創建好的,只要創建Handler對象實例并調用handleMessage方法即可進行消息處理,而子線程要自己去自定義Looper才能實現相同的功能。
子線程如何實現與其相關的Handler?
通過自定義的Looper去實現在子線程中處理消息,代碼如下:
public?class?MainActivity?extends?AppCompatActivity?{
????@Override
????protected?void?onCreate(Bundle?savedInstanceState)?{
????????super.onCreate(savedInstanceState);
????????setContentView(new?LinearLayout(this));
????????MyThread?myThread =?new?MyThread();
????????myThread.start();
????????myThread.mHandler.sendEmptyMessage(0x123);
????}
????class?MyThread?extends?Thread?{
????????public?Handler?mHandler;
????????public?Looper?mLooper;
????????@Override
????????public?void?run()?{
????????????//創建Looper的實例對象,此時會創建一條與之對應的MessageQueue
????????????mLooper.prepare();
????????????//在新建的子線程當中創建Handler實例并調用handleMessage方法
????????????mHandler =?new?Handler()?{
????????????????@Override
????????????????public?void?handleMessage(Message?msg)?{
????????????????????//此處不作任何操作
????????????????}
????????????};
????????????//調用Loop()方法,讓創建好的Looper去輪詢MessageQueue
????????????// 在Loop()之后的代碼將不會執行
????????????mLooper.loop();
????????}
????}}
在子線程中使用自定義的Looper去實現Handler機制的消息處理可能會發生什么問題?
實際上,如果我們在子線程中使用自定義的Looper去實現子線程的Handler機制的消息處理功能有可能會因為線程并發的問題導致程序出現NullPointException。在如下實例代碼中,程序會報出NullPointException的Error,Error的根源就是在UI線程中創建Handler實例時傳入的myThread.mLooper對象,由于UI線程與子線程的并發問題,在UI線程使用這個myThread.mLooper對象時子線程有可能還沒給這個對象創建出實例(創建出Looper和MessageQueue),從而使程序拋出NullPointException,實例代碼如下:
public?class?MainActivity?extends?AppCompatActivity?{
????@Override
????protected?void?onCreate(Bundle?savedInstanceState)?{
????????super.onCreate(savedInstanceState);
????????setContentView(new?LinearLayout(this));
????????MyThread?myThread =?new?MyThread();
????????myThread.start();
????????Handler?handler =?new?Handler(myThread.mLooper)?{
????????????@Override
????????????public?void?handleMessage(Message?msg)?{
????????????????//此處不作任何操作
????????????}
????????};
????????handler.sendEmptyMessage(0x123);
????}
????class?MyThread?extends?Thread?{
????????public?Handler?mHandler;
????????public?Looper?mLooper;
????????@Override
????????public?void?run()?{
????????????//創建Looper的實例對象,此時會創建一條與之對應的MessageQueue
????????????mLooper.prepare();
????????????//在新建的子線程當中創建Handler實例并調用handleMessage方法
????????????mHandler =?new?Handler()?{
????????????????@Override
????????????????public?void?handleMessage(Message?msg)?{
????????????????????//此處不作任何操作
????????????????}
????????????};
????????????//調用Loop()方法,讓創建好的Looper去輪詢MessageQueue
????????????// 在Loop()之后的代碼將不會執行
????????????mLooper.loop();
????????}
????}}
在主線程中創建Handler對象的時候傳入myThread.mLooper的作用是為該Handler對象指定一個Looper,myThread.mLooper已經替代UI線程中默認的Looper,也就是說該Hanlder對象發送消息以后會由myThread.mLooper將消息泵給myThread.mHandler的handleMessage方法進行處理。
上述的程序邏輯并無錯誤,但將其運行一遍會發現程序會報出NullpointException。
怎么樣克服子線程自定義Looper帶來的問題呢?
想要避免這個NullPointException最簡單的方法就是try/catch一下(哈哈哈,開玩笑的)。其實Android本來就有一套推薦的方案在子線程中實現Handler機制,不需要再額外地自定義Looper這么麻煩,Android推薦使用HandlerThread來在子線程中實現Handler機制。
HandlerThread是Handler類還是Thread類?它究竟是什么?
HandlerThread其實就是Thread類,它跟普通的Thread相比就是多了一個Looper,但就是多了這么一個Looper讓我們省卻自定義Looper和擔心由于線程并發而造成Looper在未創建成功就被調用的的麻煩。
怎么使用HandlerThread?
HandlerThread的使用方法很簡單,大致分為4步,直接上碼:
public?class?MainActivity?extends?AppCompatActivity?{
????@Override
????protected?void?onCreate(Bundle?savedInstanceState)?{
????????super.onCreate(savedInstanceState);
????????setContentView(new?LinearLayout(this));
????????//第一步,創建HandlerThread對象,并為它指定名稱 ?
????????HandlerThread?handlerThread =?new?HandlerThread("handler_thread");
????????//第二步,調用start()方法開啟線程
????????handlerThread.start();
????????//第三步,創建Handler對象,傳入handlerThread.getLooper()參數
????????Handler?handler =?new?Handler(handlerThread.getLooper())?{
????????????@Override
????????????public?void?handleMessage(Message?msg)?{
????????????????//此處不作任何操作
????????????}
????????};
????????//第四步,發送消息
????????handler.sendEmptyMessage(0x123);
????}}
其實在上述的第四步發送消息的這一步其實并不只是可以用sendEmptyMessage方式,可以新開線程去發送消息,也可以調用post方法以后再發送消息,但最最重要的是要明白,在第三步中,把原本是綁定UI線程Looper的Handler設置成綁定HandlerThread的Looper。所以程序的Handler現在是與HandlerThread進行關聯,這也就達到了在子線程上實現Handler機制的目的??!
從線程通信的角度來看待Handler和HandlerThread分別解決了什么?
Handler的目標是解決子線程與主線程通信的問題,而HandlerThread則是解決子線程和子線程之間的通信問題
最后總結兩句:
MessageQueue、Looper和Handler是組成Handler機制的鐵人三項,雖然我們平時比較少接觸到MessageQueue和Looper,但是其作用,原理以及用法還是很重要的,MessageQueue負責存儲由Handler發送過來的Message,Looper則不斷輪詢MessageQueue,將MessageQueue中的Message交由Handler進行處理,Handler根據Looper傳來的Message進行信息處理或UI更新
觀察HandlerThread的官方文檔的兩句:①Thread. Handy class for starting a new thread that has a looper.②The looper can then be used to create handler classes.
釋義:HandlerThread對象start后可以獲得其Looper對象,并且使用這個Looper對象實例Handler,之后Handler就可以運行在其他線程中了。
HandlerThread繼承于Thread,所以它本質就是個Thread。與普通Thread的差別就在于,然后在內部直接實現了Looper的實現,這是Handler消息機制必不可少的。有了自己的looper,可以讓我們在自己的線程中分發和處理消息。如果不用HandlerThread的話,需要手動去調用Looper.prepare()和Looper.loop()這些方法。
// 子線程中創建新的Handler 沒有使用HandlerThread
new Thread () {
? ? @Override
? ? public void run() {
? ? ? ? Looper.prepare();
? ? ? ? Hnadler handler = new Handler();
? ? ? ? Looper.loop();
? ? }
}
mSMTHandlerThread = new HandlerThread("SMT-Handler-thread");
? ? ? ? mSMTHandlerThread.start();
? ? ? ? mSMTHandler = new Handler(mSMTHandlerThread.getLooper());
Handler里部分源碼:
public Handler() {
? ? ? ? throw new RuntimeException("Stub!");
? ? }
? ? public Handler(Handler.Callback callback) {
? ? ? ? throw new RuntimeException("Stub!");
? ? }
? ? public Handler(Looper looper) {
? ? ? ? throw new RuntimeException("Stub!");
? ? }
? ? public Handler(Looper looper, Handler.Callback callback) {
? ? ? ? throw new RuntimeException("Stub!");
? ? }
?當調用quitSafely方法時,其內部調用的是Looper的quitSafely方法而最終執行的是MessageQueue中的removeAllFutureMessagesLocked方法,
該方法只會清空MessageQueue消息池中所有的延遲消息,并將消息池中所有的非延遲消息派發出去讓Handler去處理完成后才停止Looper循環,
quitSafely相比于quit方法安全的原因在于清空消息之前會派發所有的非延遲消息。最后需要注意的是Looper的quit方法是基于API 1,
而Looper的quitSafely方法則是基于API 18的。
quit()和quitSafely()的區別在于,quit()會清空所有消息(無論延時或非延時),quitSafely()只清空延時消息,無論是調用了quit()方法還是quitSafely()方法,
Looper就不再接收新的消息。即在調用了Looper的quit()或quitSafely()方法之后,消息循環就終結了,
這時候再通過Handler調用sendMessage或post等方法發送消息時均返回false,線程也就結束了
mSMTHandler.removeCallbacks(timeoutRunnable);