Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> Android消息機制之Handler,androidhandler

Android消息機制之Handler,androidhandler

編輯:關於android開發

Android消息機制之Handler,androidhandler


Android為什麼要提供Handler

Android建議我們不要在UI線程中執行耗時操作,因為這很容易導致ANR異常(在Android源碼中我們可以看到,UI如果對用戶的操作超過5秒無響應,就會報ANR異常)。因此,一些耗時操作都會在子線程中完成。當我們在子線程中獲取了數據,要將其顯示到UI中,如果沒有Handler,這將很難完成。因此,Android之所以提供Handler,就是為了解決子線程訪問UI的問題。
為什麼Android不允許在子線程中訪問UI呢?顯然這樣做不安全,多線程訪問UI是不安全的(學過操作系統的盆友應該都了解線程互斥,這裡我就不詳細介紹了)。有人就會說了,可以通過設置信號量來解決啊。這中方法不是不可以,因為這種方法會使訪問UI的邏輯變得復雜;其次這會降低UI的訪問效率。而使用Handler就比較簡單高效。Handler是同個Message來通訊的。

 

Handler的用法

使用Handler時,需要重寫handleMessage方法,在handleMessage中接受新線程發來的Message,並做相應的處理。在新線程中則是通過Message來傳遞消息,Message中往往也攜帶著需要傳遞的數據以及消息的類型。還要強調一點,如果當前線程有Looper就不需要執行Looper.prepare(),如果沒有,就需要在新線程內執行Looper.prepare(),否則會報錯。具體使用代碼如下:

 1 public class MainActivity extends AppCompatActivity {
 2     private Handler mHandler=new Handler(){
 3         @Override
 4         public void handleMessage(Message msg) {
 5             switch (msg.what)
 6             {
 7                 case 1:
 8                     //執行需要修改的UI操作
 9                     break;
10                 default:
11                     break;
12             }
13         }
14     };
15 
16     @Override
17     protected void onCreate(Bundle savedInstanceState) {
18         super.onCreate(savedInstanceState);
19         setContentView(R.layout.activity_main);
20 
21         new Thread(new Runnable() {
22             @Override
23             public void run() {//在新線程中執行耗時操作
24 
25                 //如果當前線程有Looper就不需要執行Looper.prepare();
26                 Looper.prepare();
27                 try {
28                     Thread.sleep(1000);//睡眠1秒
29                 } catch (InterruptedException e) {
30                     e.printStackTrace();
31                 }
32 
33                 //操作完成之後通過發送Message,來通知Handler進行UI操作
34 
35                 Message msg=new Message();
36                 msg.what=1;
37 
38                 /*這部分是偽代碼,value 是想通過Message傳遞的值
39                 Bundle data=new Bundle();
40                 data.putSerializable("key",value);
41                 msg.setData(data);
42 
43                 */
44 
45                 //設置好數據後,發送消息
46                 mHandler.sendMessage(msg);
47             }
48         }).start();
49     }
50 
51 }

當然,handler也可以在子線程中創建,代碼如下:

 1   private TextView tv_test;
 2 
 3     @Override
 4     protected void onCreate(Bundle savedInstanceState) {
 5         super.onCreate(savedInstanceState);
 6         setContentView(R.layout.handler_test_layout);
 7 
 8         tv_test= (TextView) findViewById(R.id.tv_test);
 9 
10 
11     }
12 
13     //button點擊的函數
14     public void click(View v)
15     {
16         new Thread(new Runnable() {
17             @Override
18             public void run() {
19                 Looper.prepare();
20                 Handler handler=new Handler(Looper.getMainLooper()){
21                     @Override
22                     public void handleMessage(Message msg) {
23                         switch (msg.what)
24                         {
25                             case 1:
26                                 tv_test.setText("receive msg");
27                         }
28                     }
29                 };
30                 Message msg=new Message();
31                 msg.what=1;
32                 handler.sendMessage(msg);
33             }
34         }).start();
35     }

上面的代碼是,當點擊按鈕後,就會創建一個新的線程,在新線程中創建handler,並發送消息、接受消息。這裡需要注意的是,在新線程中創建handler需要使用Handler handler=new Handler(Looper.getMainLooper())這樣的寫法,Looper.getMainLooper()將主線程中的Looper傳過去,這樣handler才能訪問UI。運行效果如下:

 

 

Handler的內部機制

Handler創建時會采用Looper來建立消息循環。所以,當前線程必須要有Looper。當Handler創建完成後,其內部的Looper以及MessageQueue既可以和Handler一起協同工作了。Handler通過sendMessage將消息發送給內部的MessageQueue,而MessageQueue會調用queue.enqueueMessage(msg, uptimeMillis)方法,它的源碼如下:

 1 boolean enqueueMessage(Message msg, long when) {
 2         if (msg.target == null) {
 3             throw new IllegalArgumentException("Message must have a target.");
 4         }
 5         if (msg.isInUse()) {
 6             throw new IllegalStateException(msg + " This message is already in use.");
 7         }
 8 
 9         synchronized (this) {
10             if (mQuitting) {
11                 IllegalStateException e = new IllegalStateException(
12                         msg.target + " sending message to a Handler on a dead thread");
13                 Log.w(TAG, e.getMessage(), e);
14                 msg.recycle();
15                 return false;
16             }
17 
18             msg.markInUse();
19             msg.when = when;
20             Message p = mMessages;
21             boolean needWake;
22             if (p == null || when == 0 || when < p.when) {
23                 // New head, wake up the event queue if blocked.
24                 msg.next = p;
25                 mMessages = msg;
26                 needWake = mBlocked;
27             } else {
28                 // Inserted within the middle of the queue.  Usually we don't have to wake
29                 // up the event queue unless there is a barrier at the head of the queue
30                 // and the message is the earliest asynchronous message in the queue.
31                 needWake = mBlocked && p.target == null && msg.isAsynchronous();
32                 Message prev;
33                 for (;;) {
34                     prev = p;
35                     p = p.next;
36                     if (p == null || when < p.when) {
37                         break;
38                     }
39                     if (needWake && p.isAsynchronous()) {
40                         needWake = false;
41                     }
42                 }
43                 msg.next = p; // invariant: p == prev.next
44                 prev.next = msg;
45             }
46 
47             // We can assume mPtr != 0 because mQuitting is false.
48             if (needWake) {
49                 nativeWake(mPtr);
50             }
51         }
52         return true;
53     }

通過源碼,我們發現,queue.enqueueMessage(msg, uptimeMillis)將消息放入了MessageQueue裡。Looper則會一直處理MessageQueue中的消息。


  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved