Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android編程入門 >> Android消息機制之Handler

Android消息機制之Handler

編輯:Android編程入門

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(),否則會報錯。具體使用代碼如下:

 public class MainActivity extends AppCompatActivity {
     private Handler mHandler=new Handler(){
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what)
             {
                 case 1:
                     //執行需要修改的UI操作
                     break;
                 default:
                     break;
             }
         }
     };
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
 
         new Thread(new Runnable() {
             @Override
             public void run() {//在新線程中執行耗時操作
 
                 //如果當前線程有Looper就不需要執行Looper.prepare();
                 Looper.prepare();
                 try {
                     Thread.sleep(1000);//睡眠1秒
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
 
                 //操作完成之後通過發送Message,來通知Handler進行UI操作
 
                 Message msg=new Message();
                 msg.what=1;
 
                 /*這部分是偽代碼,value 是想通過Message傳遞的值
                 Bundle data=new Bundle();
                 data.putSerializable("key",value);
                 msg.setData(data);
 
                 */
 
                 //設置好數據後,發送消息
                 mHandler.sendMessage(msg);
             }
         }).start();
     }
 
 }

Handler的內部機制

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

 boolean enqueueMessage(Message msg, long when) {
         if (msg.target == null) {
             throw new IllegalArgumentException("Message must have a target.");
         }
         if (msg.isInUse()) {
             throw new IllegalStateException(msg + " This message is already in use.");
         }
 
         synchronized (this) {
             if (mQuitting) {
                 IllegalStateException e = new IllegalStateException(
                         msg.target + " sending message to a Handler on a dead thread");
                 Log.w(TAG, e.getMessage(), e);
                 msg.recycle();
                 return false;
             }
 
             msg.markInUse();
             msg.when = when;
             Message p = mMessages;
             boolean needWake;
             if (p == null || when == 0 || when < p.when) {
                 // New head, wake up the event queue if blocked.
                 msg.next = p;
                 mMessages = msg;
                 needWake = mBlocked;
             } else {
                 // Inserted within the middle of the queue.  Usually we don't have to wake
                 // up the event queue unless there is a barrier at the head of the queue
                 // and the message is the earliest asynchronous message in the queue.
                 needWake = mBlocked && p.target == null && msg.isAsynchronous();
                 Message prev;
                 for (;;) {
                     prev = p;
                     p = p.next;
                     if (p == null || when < p.when) {
                         break;
                     }
                     if (needWake && p.isAsynchronous()) {
                         needWake = false;
                     }
                 }
                 msg.next = p; // invariant: p == prev.next
                 prev.next = msg;
             }
 
             // We can assume mPtr != 0 because mQuitting is false.
             if (needWake) {
                 nativeWake(mPtr);
             }
         }
         return true;
     }

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




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