解析Android的 訊息傳遞機制Handler

來源:互聯網
上載者:User

標籤:android handler 訊息傳遞

1. 什麼是Handler:

Handler 網路釋義“操縱者,管理者的”意思,在Android裡面用於管理多線程對UI的操作;

2. 為什麼會出現Handler:

在Android的設計機制裡面,只允許主線程(一個程式第一次啟動時所移動的線程,因為此線程主要是完成對UI相關事件的處理,所以也稱UI線程)

對UI進行修改等操作,這是一種規則的簡化,之所以這樣簡化是因為Android的UI操作時線程不安全的,為了避免多個線程同時操作UI造成安全執行緒

問題,才出現了這個簡化的規則。

由此以來,問題就出現了,因為只允許主線程修改UI,那麼如果新線程的操作需要修改原來的UI該如何進行的?舉個常見的例子就是:如果新線程的操作是更新UI中TextView

的值,那麼該如何操作?

這時候就需要Handler在新線程和主線程(UI線程)之間傳遞休息;

3. Handler的功能:

主要就是兩個:

1)在新啟動的線程中發送訊息;

2)在主線程中擷取,處理訊息;

看似簡單,但是如何處理同步問題卻是一個難題,即如何把握新線程發送訊息的時機和主線程處理訊息的時機。這個問題的解決方案是:

在主線程和新線程之間使用一個叫做MessageQueue的隊列,新啟動的線程發送訊息時將訊息先發送到與之關聯的MessageQueue,然後主線程的Handler方法會被調用

從MessageQueue中去取相應的訊息進行處理。

4. Handler的實現機制

Handler的實現主要是依靠下面的幾個方法:

讀取訊息使用到的方法是;  

void handleMessage(Message msg) ,進程通過重寫這個方法來處理訊息。

final boolean hasMessage(int what), 檢查訊息佇列中是否包含what屬性為指定值的訊息。

final boolean hasMessage(int what,Object object),減產隊列中是否有指定值和指定對象的訊息。

Message obtainMessage(): 擷取訊息,課被多種方式重載。

發送訊息用到的方法有:

sendEmptyMessage(int what): 發送空訊息;

final boolean sendEmptyMessageDelayed(int what, long delayMillis):指定多少毫秒之後發送空訊息

final boolean sendMessage(Message msg):立即發送訊息

final boolean sendMessageDelayed(Message msg, long delayMillis)指定多少毫秒之後發送空訊息

public class HandlerTest extends Activity{ImageView show;// 代表從網路下載得到的圖片Bitmap bitmap;Handler handler = new Handler(){@Override<span style="color:#ff0000;">public void handleMessage(Message msg)</span>{if(msg.what == 0x123)  //如果該訊息是本程式發的{// 使用ImageView顯示該圖片show.setImageBitmap(bitmap);}}};@Overridepublic void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.main);show = (ImageView) findViewById(R.id.show);<span style="color:#ff0000;">new Thread()</span>{public void run(){try{// 定義一個URL對象URL url = new URL("http://img001.21cnimg.com/photos"+<span style="white-space:pre"></span>"/album/20140626/o/C164BDB0B24F59929C2113C0A9910636.jpeg");// 開啟該URL對應的資源的輸入資料流InputStream is = url.openStream();// 從InputStream中解析出圖片bitmap = BitmapFactory.decodeStream(is);// 發送訊息、通知UI組件顯示該圖片<span style="color:#ff0000;">handler.sendEmptyMessage(0x123);</span>is.close();}catch (Exception e){e.printStackTrace();}}}.start();}}

新的進程在將圖片從網上解析下來之後向主進程發送空訊息,之後主線程中handleMessage()的方法會被自動調用,更新UI。

5.  深入理解Handler的工作機制

上面我們看到了一個簡單的Handler的工作過程,其中Handler是在主線程中定義的。如果Handler是在子線程中定義的那麼可以更深入的理解他的工作原理。

因為有的時候我們需要將主線程中的訊息傳遞給子線程,讓子線程去處理一些計算量比較大的任務,因為應用程式應盡量避免在UI線程中執行耗時操作,否則會

導致ANR異常(Application Not Responding)。這樣的話,我們上面所講的 主線程和新線程的角色就發生了顛倒,主線程需要向新線程發送訊息,然後新線程

進行訊息的處理。在這種情況下,Handler需要定義在新線程中,在這種情況下需要做一些額外的工作。

我們先來解釋一下配合Handler的其它組件:

Looper: 每個線程對應一個looper,它負責管理MessageQueue,將訊息從隊列中取出交給Handler進行處理;

MessageQueue:負責管理Message,接收Handler發送過來的message;

是整個工作的流程:


下面我們具體闡述一下工作的過程:

在建立一個Handler之前需要先建立Looper,建立的方式是Looper.prepare();

在建立Looper的同時會自動建立MessageQueue;

下面 建立一個Handler的對象

然後調用Looper的loop()方法

下面的這段代碼是一個執行個體,摘自 瘋狂android講義;主進程將上限發送給子線程計算2-上限之間的素數

public class CalPrime extends Activity{static final String UPPER_NUM = "upper";EditText etNum;CalThread calThread;// 定義一個線程類class CalThread extends Thread{<span style="color:#cc0000;">public Handler mHandler;</span>public void run(){<span style="color:#ff0000;">Looper.prepare();</span><span style="color:#ff0000;">mHandler = new Handler()</span>{// 定義處理訊息的方法@Overridepublic void handleMessage(Message msg){if(msg.what == 0x123){int upper = msg.getData().getInt(UPPER_NUM);List<Integer> nums = new ArrayList<Integer>();// 計算從2開始、到upper的所有質數outer:for (int i = 2 ; i <= upper ; i++){// 用i處於從2開始、到i的平方根的所有數for (int j = 2 ; j <= Math.sqrt(i) ; j++){// 如果可以整除,表明這個數不是質數if(i != 2 && i % j == 0){continue outer;}}nums.add(i);}// 使用Toast顯示統計出來的所有質數Toast.makeText(CalPrime.this , nums.toString(), Toast.LENGTH_LONG).show();}}};<span style="color:#ff0000;">Looper.loop();</span>}}@Overridepublic void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.main);etNum = (EditText)findViewById(R.id.etNum);<span style="color:#ff0000;">calThread = new CalThread();</span>// 啟動新線程<span style="color:#ff0000;">calThread.start();</span>}// 為按鈕的點擊事件提供事件處理函數public void cal(View source){// 建立訊息<span style="color:#ff0000;">Message msg = new Message();</span>msg.what = 0x123;Bundle bundle = new Bundle();bundle.putInt(UPPER_NUM ,Integer.parseInt(etNum.getText().toString()));<span style="color:#ff0000;">msg.setData(bundle);</span>// 向新線程中的Handler發送訊息<span style="color:#ff0000;">calThread.mHandler.sendMessage(msg);</span>}}

聯繫我們

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