深入瞭解android中的訊息機制Handler

來源:互聯網
上載者:User

深入瞭解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,這其中的詳情就靠大家去探索了…..

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.