最近在做毕设,关于android的,其中觉得android的消息机制很有意思,这里就写下自己的想法
和Windows一样android也是消息驱动的。Android通过Handler和looper实现消息循环机制。

一、Handler的创建

每个Handler都会和一个线程和线程的message queue关联起来,此时你可以传递messages 和 runnable对象到message queue中。后面可以从message queue中拿出该对象并且执行。

上面提到了Handler, Message queue这两个对象,那他们之间是怎么交互的呢?

这中间就涉及到了looper的概念。我们首先来看下handler和looper,message queue        这两个类的关系

Handler提供了很多的构造函数,


1. public Handler 
     ( 
     ) 
     {
2. myLooper 
     ( 
     ) 
     ;
3. if 
     (mLooper == 
     null 
     ) 
     {
4. throw 
     new 
     RuntimeException 
     (
5. "Can't create handler inside thread that has not called Looper.prepare()" 
     ) 
     ;
6. }
7. mQueue 
     ;
8. null 
     ;
9. }
10. 
        
    
11. public Handler 
     (Looper looper 
     ) 
     {
12. ;
13. mQueue 
     ;
14. null 
     ;
15. }


从这两个构造函数中,我们发现当为提供looper的时候,android会通过Looper.myLooper()获得当前线程的looper。那通过myLooper()获得的当前线程的looper 可能会为 null 吗? 答案是肯定的,若在主线程下启动一个新的线程,那这个子线程是不会建立自己的Message Queue的。Android通过ActivityThread来启动一个activity,在activity的main()方法中有2句关键的代码

Looper. prepareMainLooper()  //具体查看源代码

Looper.loop()

因为创建子线程的时候并没有运行前面这两句话,所以子线程中调用Looper.myLooper()返回的是null。所以就造成了在子线程中创default handler是错误的。而要在子线程中有自己的looper就需要如下写,



    1. class LooperThread 
         extends 
         Thread 
         {
    2. public Handler mHandler 
         ;
    3. 
                  
        
    4. public 
         void run 
         ( 
         ) 
         {
    5. prepare 
         ( 
         ) 
         ;
    6. 
                      
        
    7. new Handler 
         ( 
         ) 
         {
    8. public 
         void handleMessage 
         (Message msg 
         ) 
         {
    9. // process incoming messages here
    10. }
    11. } 
         ;
    12. 
                      
        
    13. loop 
         ( 
         ) 
         ;
    14. }



    Looper源代码中有一个关键的方法prepare(),那他具体做什么事呢,代码如下



      1. public 
           static 
           final 
           void prepare 
           ( 
           ) 
           {
      2. if 
           (sThreadLocal. 
           get 
           ( 
           ) 
           != 
           null 
           ) 
           {
      3. throw 
           new 
           RuntimeException 
           ( 
           "Only one Looper may be created per thread" 
           ) 
           ;
      4. }
      5. set 
           ( 
           new Looper 
           ( 
           ) 
           ) 
           ;
      6. }



      通过ThreadLocal实现了一个线程只有一个Looper

      二、Handler的消息发送

      总感觉Handler就像一个交警一样,负责message的创建,获得,处理。Handler的sendMessage()方法最终其实就是调用了queue.enqueueMessage(msg, uptimeMillis);把消息放入message queue中。Looper通过Looper.loop()检测到有消息并将消息广播后,Handler又负责接收到此消息并调用handleMessage进行处理接下来的事情。Message的创建一般都是通过如下代码建立的,把Handler传进去



        1. public 
             final Message obtainMessage 
             ( 
             )
        2. {
        3. return Message. 
             obtain 
             ( 
             this 
             ) 
             ;
        4. }



        三、Message

        Message在里面是一个链表的结构。在这个方法中,会首先去MessagePool(消息回收站)去找Message,如果有被回收的Message,则会将这个Message取出来进行再利用。如果没有被回收的,这时系统才会真正new一个Message对象出来当然MessagePool不是无限大的,它最多只能保存十条回收的消息,多出来的就直接删除了

        更新UI例子



        1. public 
             class ReviewList 
             extends ListActivity 
             {
        2. 
                
            
        3. private 
             static 
             final 
             String CLASSTAG = ReviewList. 
             class. 
             getSimpleName 
             ( 
             ) 
             ;
        4. private 
             static 
             final 
             int MENU_CHANGE_CRITERIA = 
             Menu. 
             FIRST + 
             1 
             ;
        5. private 
             static 
             final 
             int MENU_GET_NEXT_PAGE = 
             Menu. 
             FIRST 
             ;
        6. private 
             static 
             final 
             int NUM_RESULTS_PER_PAGE = 
             8 
             ;
        7. 
                  
            
        8. private TextView empty 
             ;
        9. private ProgressDialog progressDialog 
             ;
        10. private ReviewAdapter reviewAdapter 
             ;
        11. private List 
             <Review 
             > reviews 
             ;
        12. 
                  
            
        13. private 
             final Handler handler = 
             new Handler 
             ( 
             ) 
             {
        14. 
                      @Override 
            
        15. public 
             void handleMessage 
             ( 
             final Message msg 
             ) 
             {
        16. v 
             (Constants. 
             LOGTAG, 
             " " + ReviewList. 
             CLASSTAG + 
             " worker thread done, setup ReviewAdapter" 
             ) 
             ;
        17. dismiss 
             ( 
             ) 
             ;
        18. if 
             ( 
             (reviews == 
             null 
             ) || 
             (reviews. 
             size 
             ( 
             ) == 
             0 
             ) 
             ) 
             {
        19. setText 
             ( 
             "No Data" 
             ) 
             ;
        20. } 
             else 
             {
        21. new ReviewAdapter 
             (ReviewList. 
             this, reviews 
             ) 
             ;
        22. (reviewAdapter 
             ) 
             ;
        23. }
        24. }
        25. } 
             ;
        26. 
                
            
        27. 
                  @Override 
            
        28. public 
             void onCreate 
             (Bundle savedInstanceState 
             ) 
             {
        29. super. 
             onCreate 
             (savedInstanceState 
             ) 
             ;
        30. v 
             (Constants. 
             LOGTAG, 
             " " + ReviewList. 
             CLASSTAG + 
             " onCreate" 
             ) 
             ;
        31. 
                
            
        32. // NOTE* This Activity MUST contain a ListView named "@android:id/list"
        33. // (or "list" in code) in order to be customized
        34. // http://code.google.com/android/reference/android/app/ListActivity.html
        35. this. 
             setContentView 
             (R. 
             layout. 
             review_list 
             ) 
             ;
        36. 
                
            
        37. this. 
             empty = 
             (TextView 
             ) findViewById 
             (R. 
             id. 
             empty 
             ) 
             ;
        38. 
                
            
        39. // set list properties
        40. final 
             ListView listView = getListView 
             ( 
             ) 
             ;
        41. setItemsCanFocus 
             ( 
             false 
             ) 
             ;
        42. setChoiceMode 
             ( 
             ListView. 
             CHOICE_MODE_SINGLE 
             ) 
             ;
        43. setEmptyView 
             ( 
             this. 
             empty 
             ) 
             ;
        44. }
        45. 
                
            
        46. 
                  @Override 
            
        47. protected 
             void onResume 
             ( 
             ) 
             {
        48. super. 
             onResume 
             ( 
             ) 
             ;
        49. v 
             (Constants. 
             LOGTAG, 
             " " + ReviewList. 
             CLASSTAG + 
             " onResume" 
             ) 
             ;
        50. // get the current review criteria from the Application (global state placed there)
        51. (RestaurantFinderApplication 
             ) getApplication 
             ( 
             ) 
             ;
        52. String criteriaCuisine = application. 
             getReviewCriteriaCuisine 
             ( 
             ) 
             ;
        53. String criteriaLocation = application. 
             getReviewCriteriaLocation 
             ( 
             ) 
             ;
        54. 
                
            
        55. // get start from, an int, from extras
        56. int startFrom = getIntent 
             ( 
             ). 
             getIntExtra 
             (Constants. 
             STARTFROM_EXTRA, 
             1 
             ) 
             ;
        57. 
                
            
        58. (criteriaLocation, criteriaCuisine, startFrom 
             ) 
             ;
        59. }
        60. 
                  
            
        61. 
                  @Override 
            
        62. public 
             boolean onCreateOptionsMenu 
             ( 
             Menu menu 
             ) 
             {
        63. super. 
             onCreateOptionsMenu 
             (menu 
             ) 
             ;
        64. add 
             ( 
             0, ReviewList. 
             MENU_GET_NEXT_PAGE, 
             0, R. 
             string. 
             menu_get_next_page 
             ). 
             setIcon 
             (
        65. R. 
             drawable. 
             ic_menu_more 
             ) 
             ;
        66. add 
             ( 
             0, ReviewList. 
             MENU_CHANGE_CRITERIA, 
             0, R. 
             string. 
             menu_change_criteria 
             ). 
             setIcon 
             (
        67. R. 
             drawable. 
             ic_menu_edit 
             ) 
             ;
        68. return 
             true 
             ;
        69. }
        70. 
                
            
        71. 
                  @Override 
            
        72. public 
             boolean onMenuItemSelected 
             ( 
             int featureId, 
             MenuItem item 
             ) 
             {
        73. null 
             ;
        74. switch 
             (item. 
             getItemId 
             ( 
             ) 
             ) 
             {
        75. case
        76. // increment the startFrom value and call this Activity again
        77. new Intent 
             (Constants. 
             INTENT_ACTION_VIEW_LIST 
             ) 
             ;
        78. putExtra 
             (Constants. 
             STARTFROM_EXTRA, getIntent 
             ( 
             ). 
             getIntExtra 
             (Constants. 
             STARTFROM_EXTRA, 
             1 
             )
        79. NUM_RESULTS_PER_PAGE 
             ) 
             ;
        80. (intent 
             ) 
             ;
        81. return 
             true 
             ;
        82. case
        83. new Intent 
             ( 
             this, ReviewCriteria. 
             class 
             ) 
             ;
        84. (intent 
             ) 
             ;
        85. return 
             true 
             ;
        86. }
        87. return 
             super. 
             onMenuItemSelected 
             (featureId, item 
             ) 
             ;
        88. }
        89. 
                  
            
        90. 
                  @Override 
            
        91. protected 
             void onListItemClick 
             ( 
             ListView l, 
             View v, 
             int position, 
             long id 
             ) 
             {
        92. // set the current review to the Application (global state placed there)
        93. (RestaurantFinderApplication 
             ) getApplication 
             ( 
             ) 
             ;
        94. setCurrentReview 
             ( 
             this. 
             reviews. 
             get 
             (position 
             ) 
             ) 
             ;
        95. 
                
            
        96. // startFrom page is not stored in application, for example purposes it's a simple "extra"
        97. new Intent 
             (Constants. 
             INTENT_ACTION_VIEW_DETAIL 
             ) 
             ;
        98. putExtra 
             (Constants. 
             STARTFROM_EXTRA, getIntent 
             ( 
             ). 
             getIntExtra 
             (Constants. 
             STARTFROM_EXTRA, 
             1 
             ) 
             ) 
             ;
        99. (intent 
             ) 
             ;
        100. }
        101. 
                  
            
        102. private 
             void loadReviews 
             ( 
             String location, 
             String cuisine, 
             int startFrom 
             ) 
             {
        103. 
                
            
        104. v 
             (Constants. 
             LOGTAG, 
             " " + ReviewList. 
             CLASSTAG + 
             " loadReviews" 
             ) 
             ;
        105. 
                
            
        106. final ReviewFetcher rf = 
             new ReviewFetcher 
             (location, cuisine, 
             "ALL", startFrom, 
            
        107. NUM_RESULTS_PER_PAGE 
             ) 
             ;
        108. 
                
            
        109. this. 
             progressDialog = ProgressDialog. 
             show 
             ( 
             this, 
             " Working…", 
             " Retrieving reviews", 
             true, 
             false 
             ) 
             ;
        110. 
                
            
        111. // get reviews in a separate thread for ProgressDialog/Handler
        112. // when complete send "empty" message to handler
        113. new 
             Thread 
             ( 
             ) 
             {
        114. 
                          @Override 
            
        115. public 
             void run 
             ( 
             ) 
             {
        116. getReviews 
             ( 
             ) 
             ;
        117. sendEmptyMessage 
             ( 
             0 
             ) 
             ;
        118. }
        119. }. 
             start 
             ( 
             ) 
             ;
        120. }
        121. }



        在android里,新产生一个线程并不会自动建立其Message Loop。Android里并没有Global 的Message Queue,所以不同的APK里不能透过Message Queue来交换信息