Handler 詳解,handler詳解

來源:互聯網
上載者:User

Handler 詳解,handler詳解

這個詳解基本小結了Handler的使用和相關的知識,在瞭解這些知識前,我們先來看看這些問題.

  • Handler 是什嗎?
  • 子線程更新UI有幾種典型方式,這些方式本質上是什麼樣的?
  • 子線程真的不能更新UI嗎?
  • HandlerThread是什嗎?
  • 主線程和子線程之間如何相互連信?

好了,如果上面的問題,你能對答如流,並且深知其中的原理那麼沒有必要繼續看下去了,反之,就該好好補補了(^o^)/~。

Handler是什麼

handler是Android給我們提供來更新UI的一套機制,也是一套訊息處理的機制,我們可以發送訊息,也可以通過塔來處理訊息,handler在我們的framework中是非常常見的。

看完上面的解釋,我們有個疑問,為什麼要用Handler了?

因為android在設計的時候,就封裝了一套訊息建立、傳遞、處理機制,如果不遵循這樣的機制就沒有辦法更新UI資訊,就會拋出異常資訊;

接下來我們就從源碼的角度看一下Google是如何線上程中封裝這麼一套Handler機制的,我們先來找一下Java的入口函數Main方法。


在android.app.ActivityThread.java中。

接著我們重點關注 L4 L10 L20這三行代碼,我們先一步一步跟進去

L4:

然後看看L88和L93

由上面的代碼可以看到,一開始就初始化了一個Looper執行個體,然後將這個執行個體儲存在sThreadLocal成員變數中,然後在需要的時候調用myLoop()方法,再把這個Looper執行個體取出來。所以很容易得到,一個線程對應唯一的Looper執行個體

L10:
在看第10行代碼sMainThreadHandler = thread.getHandler();之前,先看看第6行代碼
ActivityThread thread = new ActivityThread();
然後我們看一下ActivityThread這個類的成員變數:

第27行,可以看到一個很奇怪的類 H 類,成員變數 final H mH = new H();
接下來看看這個最精簡的類:

是不是看到了我們熟悉的Activity的生命週期,對的,Activity的生命週期就是在這裡觸發的,所以最開始的L10sMainThreadHandler = thread.getHandler();取回的Handler執行個體就是這麼一個H類的執行個體。

final Handler getHandler() {        return mH;}

L20:

上面的代碼首先取得Looper執行個體,然後取得該執行個體的MessageQueue,然後死迴圈遍曆這個queue。

至此,UI線程的Looper準備完畢。

子線程更新UI有幾種典型方式,這些方式本質上是什麼樣的

既然Google為我們準備了Handler機制,所以我們在子線程中和主線程互動變得很簡單了,那麼子線程如何通過Handler和UI線程互動呢,又有哪些方法,這些互動的方式本質上是什麼樣子的呢,下面我們一起來探個究竟。

從Google給我們提供的SDK中我們大概知道有以下幾種方式:

我們直接看一下上面四種互動方式的Demo:



這裡特別補充一下L24添加父類構造器方法:

public UpdateViewHandler(TextView textView){    super();    this.textView = new WeakReference<>(textView);}

上面代碼是java1.8引入Lambda運算式的寫法。代碼比較簡單,這裡不在贅述,我們接下來感興趣的是,上面的四種互動方法的源碼是神馬樣子的?

  • 我們先看 updateViewByHandlerPost 第一種方式

上面代碼需要注意的是L37行msg.target = this;,這行代碼說明,Handler發送訊息總得有個接收方,而這個接收方不是別人,就是自己(Handler執行個體本身)。

  • 接下來看updateViewByHandlerMessage

上面的代碼需要注意的是L3,這裡的Message執行個體有兩種方式,分別是
Message message = Message.obtain();Message message = new Message();這兩種方式哪種更好還是沒有區別?為什麼,自己想一想。

  • 接下來看updateViewByRunOnUiThread

如果不在UI線程,還是調用Handler的post

  • 最後來看updateViewByViewPost

還是調用Handler的post,所以上面四種方式,本質上都是調用Handler的通訊機制,所以在Android中子線程更新UI本質上都是通過Handler機制來處理。

子線程真的不能更新UI嗎

通過上面的子線程更新UI四種方式,我們基本瞭解了Android的Handler機制,但是子線程真的不能更新UI嗎?我們來做個實驗好了。

運行以後,小夥伴都驚呆了,居然完美運行,這是神馬情況?

我們先不直接回答這個問題,換個方式實現一下:

這樣運行以後,是不是立馬Crash了?並拋出Only the original thread that created a view hierarchy can touch its views.異常,這又是神馬情況?

回答這個問題還是需要回到ActvityThread這個類中去找答案,具體的細節這裡不在贅述了,直接給出原因吧,onCreate生命週期的時候ViewParent還沒有初始化完畢,這個時候在View上更新UI的時候會引起重繪,直接來看看代碼:

注意L16行,p為空白,所以不會執行p.invalidateChild(this, damage);方法
我們看看這個方法是弄啥咧。


是不是看到熟悉的異常出現在哪裡了,以上的詭異事件是不是到此就弄清楚了,主要是ViewParent沒有初始化完畢,所以不會執行UI線程檢查,如果一旦初始化完畢,必然檢查UI線程的更新操作,這個時候任何子線程企圖更新UI的操作都無處遁形了。

HandlerThread是什麼

上面講述的都是子線程主動和UI線程溝通,而一向高貴冷豔的UI線程從來沒有主動和子線程打聲招呼,是不是過於無禮了,那麼主線程怎麼樣和子線程打招呼呢?這裡Google很任性話的給我們提供了HandlerThread,這個類就是用來方便UI線程和子線程溝通的,那麼我們可以不甩這個類嗎,直接自己動手,豐衣足食如何,我們來首先不用HandlerThread小試一下。



代碼的思路是麼有問題的,主線程和子線程先溝通,然後子線程有結果以後再和主線程溝通,這個邏輯沒有問題,但是我們run的時候提示在L66 有個null 指標異常,這個問題是由於多線程並非引起的,因為這個looper還沒有來得及初始化,所以報了個null 指標異常,那麼Google給我們提供的HandlerThread就做了同步處理,所以如果將我們自己寫的帶有Looper的子線程換成HandlerThread就不會有這個多線程並發引起的問題。

主線程和子線程之間如何相互連信

上面已經基本回答了主線程和子線程之間如何相互連信,一般建議使用HandlerThread,如果不樂意直接使用,也可以自己定義一個帶有Looper的Thread的,但是必須處理好多線程並發問題。另外,對於Handler的構造器,其中有個參數是Callback,這個Callback作用是什嗎?看看源碼一切皆清楚了

很明顯的L35行,如果這個callback返回的是true 就不會繼續往下執行了,所以這個callback有截獲的作用。

關於Handler的介紹到這裡基本結束了,當然Handler的其他知識還有不少,這裡不在贅述。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.