Android--多線程之Handler
前言
Android的消息傳遞機制是另外一種形式的“事件處理”,這種機制主要是為了解決Android應用中多線程的問題,在Android中不允許Activity新啟動的線程訪問該Activity裏的UI組件,這樣會導致新啟動的線程無法改變UI組件的屬性值。但實際開發中,很多地方需要在工作線程中改變UI組件的屬性值,比如下載網絡圖片、動畫等等。本篇博客主要介紹Handler是如何發送與處理線程上傳遞來的消息,並講解Message的幾種傳遞數據的方式,最後均會以小Demo來演示。
Handler
Handler,它直接繼承自Object,一個Handler允許發送和處理Message或者Runnable對象,並且會關聯到主線程的MessageQueue中。每個Handler具有一個單獨的線程,並且關聯到一個消息隊列的線程,就是說一個Handler有一個固有的消息隊列。當實例化一個Handler的時候,它就承載在一個線程和消息隊列的線程,這個Handler可以把Message或Runnable壓入到消息隊列,並且從消息隊列中取出Message或Runnable,進而操作它們。
Handler主要有兩個作用:
- 在工作線程中發送消息。
- 在UI線程中獲取、處理消息。
上面介紹到Handler可以把一個Message對象或者Runnable對象壓入到消息隊列中,進而在UI線程中獲取Message或者執行Runnable對象,所以Handler把壓入消息隊列有兩大體系,Post和sendMessage:
- Post:Post允許把一個Runnable對象入隊到消息隊列中。它的方法有:post(Runnable)、postAtTime(Runnable,long)、postDelayed(Runnable,long)。
- sendMessage:sendMessage允許把一個包含消息數據的Message對象壓入到消息隊列中。它的方法有:sendEmptyMessage(int)、sendMessage(Message)、sendMessageAtTime(Message,long)、sendMessageDelayed(Message,long)。
從上面的各種方法可以看出,不管是post還是sendMessage都具有多種方法,它們可以設定Runnable對象和Message對象被入隊到消息隊列中,是立即執行還是延遲執行。
Post
對於Handler的Post方式來說,它會傳遞一個Runnable對象到消息隊列中,在這個Runnable對象中,重寫run()方法。一般在這個run()方法中寫入需要在UI線程上的操作。
在Handler中,關於Post方式的方法有:
- boolean post(Runnable r):把一個Runnable入隊到消息隊列中,UI線程從消息隊列中取出這個對象後,立即執行。
- boolean postAtTime(Runnable r,long uptimeMillis):把一個Runnable入隊到消息隊列中,UI線程從消息隊列中取出這個對象後,在特定的時間執行。
- boolean postDelayed(Runnable r,long delayMillis):把一個Runnable入隊到消息隊列中,UI線程從消息隊列中取出這個對象後,延遲delayMills秒執行
- void removeCallbacks(Runnable r):從消息隊列中移除一個Runnable對象。
下面通過一個Demo,講解如何通過Handler的post方式在新啟動的線程中修改UI組件的屬性:
1 package com.bgxt.datatimepickerdemo; 2 3 import android.app.Activity; 4 import android.os.Bundle; 5 import android.os.Handler; 6 import android.view.View; 7 import android.widget.Button; 8 import android.widget.TextView; 9 10 public class HandlerPostActivity1 extends Activity { 11 private Button btnMes1,btnMes2; 12 private TextView tvMessage; 13 // 聲明一個Handler對象 14 private static Handler handler=new Handler(); 15 16 @Override 17 protected void onCreate(Bundle savedInstanceState) { 18 super.onCreate(savedInstanceState); 19 setContentView(R.layout.message_activity); 20 21 btnMes1=(Button)findViewById(R.id.btnMes1); 22 btnMes2=(Button)findViewById(R.id.btnMes2); 23 tvMessage=(TextView)findViewById(R.id.tvMessage); 24 btnMes1.setOnClickListener(new View.OnClickListener() { 25 26 @Override 27 public void onClick(View v) { 28 // 新啟動一個子線程 29 new Thread(new Runnable() { 30 @Override 31 public void run() { 32 // tvMessage.setText("..."); 33 // 以上操作會報錯,無法再子線程中訪問UI組件,UI組件的屬性必須在UI線程中訪問 34 // 使用post方式修改UI組件tvMessage的Text屬性 35 handler.post(new Runnable() { 36 @Override 37 public void run() { 38 tvMessage.setText("使用Handler.post在工作線程中發送一段執行到消息隊列中,在主線程中執行。"); 39 } 40 }); 41 } 42 }).start(); 43 } 44 }); 45 46 btnMes2.setOnClickListener(new View.OnClickListener() { 47 48 @Override 49 public void onClick(View v) { 50 new Thread(new Runnable() { 51 @Override 52 public void run() { 53 // 使用postDelayed方式修改UI組件tvMessage的Text屬性值 54 // 並且延遲3S執行 55 handler.postDelayed(new Runnable() { 56 57 @Override 58 public void run() { 59 tvMessage.setText("使用Handler.postDelayed在工作線程中發送一段執行到消息隊列中,在主線程中延遲3S執行。"); 60 61 } 62 }, 3000); 63 } 64 }).start(); 65 66 } 67 }); 68 } 69 }
Android--多線程之Handler