淺析Android中的消息機制

在分析Android消息機制以前,咱們先來看一段代碼: android

01 public class MainActivity extends Activity implements View.OnClickListener {
02      
03     private TextView stateText;
04     private Button btn;
05      
06     @Override
07     public void onCreate(Bundle savedInstanceState) {
08         super.onCreate(savedInstanceState);
09         setContentView(R.layout.main);
10         stateText = (TextView) findViewById(R.id.tv);
11         btn = (Button) findViewById(R.id.btn);
12          
13         btn.setOnClickListener(this);
14     }
15  
16     @Override
17     public void onClick(View v) {
18         new WorkThread().start();
19     }
20      
21     //工做線程
22     private class WorkThread extends Thread {
23         @Override
24         public void run() {
25             //......處理比較耗時的操做
26              
27             //處理完成後改變狀態
28             stateText.setText("completed");
29         }
30     }
31 }

這段代碼彷佛看上去很正常,可是當你運行時就會發現,它會報一個致命性的異常: 編程

1 ERROR/AndroidRuntime(421): FATAL EXCEPTION: Thread-8
2 ERROR/AndroidRuntime(421): android.view.ViewRoot$CalledFromWrongThreadException:
3 Only the original thread that created a view hierarchy can touch its views.

究竟是怎麼回事呢?緣由在於,Android系統中的視圖組件並非線程安全的,若是要更新視圖,必須在主線程中更新,不能夠在子線程中執行更新的操做。 安全

既然這樣,咱們就在子線程中通知主線程,讓主線程作更新操做吧。那麼,咱們如何通知主線程呢?咱們須要使用到Handler對象。 ide

咱們稍微修改一下上面的代碼: oop

01 public class MainActivity extends Activity implements View.OnClickListener {
02      
03     private static final int COMPLETED = 0;
04      
05     private TextView stateText;
06     private Button btn;
07      
08     private Handler handler = new Handler() {
09         @Override
10         public void handleMessage(Message msg) {
11             if (msg.what == COMPLETED) {
12                 stateText.setText("completed");
13             }
14         }
15     };
16      
17     @Override
18     public void onCreate(Bundle savedInstanceState) {
19         super.onCreate(savedInstanceState);
20         setContentView(R.layout.main);
21         stateText = (TextView) findViewById(R.id.tv);
22         btn = (Button) findViewById(R.id.btn);
23          
24         btn.setOnClickListener(this);
25     }
26  
27     @Override
28     public void onClick(View v) {
29         new WorkThread().start();
30     }
31      
32     //工做線程
33     private class WorkThread extends Thread {
34         @Override
35         public void run() {
36             //......處理比較耗時的操做
37              
38             //處理完成後給handler發送消息
39             Message msg = new Message();
40             msg.what = COMPLETED;
41             handler.sendMessage(msg);
42         }
43     }
44 }


經過上面這種方式,咱們就能夠解決線程安全的問題,把複雜的任務處理工做交給子線程去完成,而後子線程經過handler對象告知主線程,由主線程更新視圖,這個過程當中消息機制起着重要的做用。 this

下面,咱們就來分析一下Android中的消息機制。 spa

熟悉Windows編程的朋友知道Windows程序是消息驅動的,而且有全局的消息循環系統。Google參考了Windows的消息循環機制, 也在Android系統中實現了消息循環機制。Android經過Looper、Handler來實現消息循環機制。Android的消息循環是針對線程 的,每一個線程均可以有本身的消息隊列和消息循環。 .net

Android系統中的Looper負責管理線程的消息隊列和消息循環。經過Looper.myLooper()獲得當前線程的Looper對象,經過Looper.getMainLooper()獲得當前進程的主線程的Looper對象。 線程

前面提到,Android的消息隊列和消息循環都是針對具體線程的,一個線程能夠存在一個消息隊列和消息循環,特定線程的消息只能分發給本線程,不 能跨線程和跨進程通信。可是建立的工做線程默認是沒有消息隊列和消息循環的,若是想讓工做線程具備消息隊列和消息循環,就須要在線程中先調用 Looper.prepare()來建立消息隊列,而後調用Looper.loop()進入消息循環。下面是咱們建立的工做線程: 對象

01 class WorkThread extends Thread {
02           public Handler mHandler;
03  
04           public void run() {
05               Looper.prepare();
06  
07               mHandler = new Handler() {
08                   public void handleMessage(Message msg) {
09                       // 處理收到的消息
10                   }
11               };
12  
13               Looper.loop();
14           }
15       }


這樣一來,咱們建立的工做線程就具備了消息處理機制了。

那麼,爲何前邊的示例中,咱們怎麼沒有看到Looper.prepare()和Looper.loop()的調用呢?緣由在於,咱們的Activity是一個UI線程,運行在主線程中,Android系統會在Activity啓動時爲其建立一個消息隊列和消息循環。

前面提到最多的是消息隊列(MessageQueue)和消息循環(Looper),可是咱們看到每一個消息處理的地方都有Handler的存在,它 是作什麼的呢?Handler的做用是把消息加入特定的Looper所管理的消息隊列中,並分發和處理該消息隊列中的消息。構造Handler的時候能夠 指定一個Looper對象,若是不指定則利用當前線程的Looper對象建立。下面是Handler的兩個構造方法:

01 /**
02      * Default constructor associates this handler with the queue for the
03      * current thread.
04      *
05      * If there isn't one, this handler won't be able to receive messages.
06      */
07     public Handler() {
08         if (FIND_POTENTIAL_LEAKS) {
09             final Class<? extends Handler> klass = getClass();
10             if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
11                     (klass.getModifiers() & Modifier.STATIC) == 0) {
12                 Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
13                     klass.getCanonicalName());
14             }
15         }
16  
17         mLooper = Looper.myLooper();
18         if (mLooper == null) {
19             throw new RuntimeException(
20                 "Can't create handler inside thread that has not called Looper.prepare()");
21         }
22         mQueue = mLooper.mQueue;
23         mCallback = null;
24     }
25  
26 /**
27      * Use the provided queue instead of the default one.
28      */
29     public Handler(Looper looper) {
30         mLooper = looper;
31         mQueue = looper.mQueue;
32         mCallback = null;
33     }
下面是消息機制中幾個重要成員的關係圖:


一個Activity中能夠建立出多個工做線程,若是這些線程把他們消息放入Activity主線程的消息隊列中,那麼消息就會在主線程中處理了。由於主線程通常負責視圖組件的更新操做,對於不是線程安全的視圖組件來講,這種方式可以很好的實現視圖的更新。

那麼,子線程如何把消息放入主線程的消息隊列中呢?只要Handler對象以主線程的Looper建立,那麼當調用Handler的 sendMessage方法,系統就會把消息主線程的消息隊列,而且將會在調用handleMessage方法時處理主線程消息隊列中的消息。

對於子線程訪問主線程的Handler對象,你可能會問,多個子線程都訪問主線程的Handler對象,發送消息和處理消息的過程當中會不會出現數據 的不一致呢?答案是Handler對象不會出現問題,由於Handler對象管理的Looper對象是線程安全的,不論是添加消息到消息隊列仍是從消息隊 列中讀取消息都是同步保護的,因此不會出現數據不一致現象。

深刻理解Android消息處理機制對於應用程序開發很是重要,也可讓咱們對線程同步有更加深入的認識,但願這篇文章能夠對朋友們有所幫助。

相關文章
相關標籤/搜索