Android消息机制——Handler、Looper、MessageQueue

最近在做毕设,关于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.         mLooper = Looper. 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 = mLooper. mQueue ;
  8.         mCallback = null ;
  9. }
  10.  
  11.     public Handler (Looper looper ) {
  12.         mLooper = looper ;
  13.         mQueue = looper. mQueue ;
  14.         mCallback = 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.            Looper. prepare ( ) ;
  6.            
  7.             mHandler = new Handler ( ) {
  8.                 public void handleMessage (Message msg ) {
  9.                     // process incoming messages here
  10.                 }
  11.             } ;
  12.            
  13.           Looper. 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.         sThreadLocal. 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.             Log. v (Constants. LOGTAG, " " + ReviewList. CLASSTAG + " worker thread done, setup ReviewAdapter" ) ;
  17.             progressDialog. dismiss ( ) ;
  18.             if ( (reviews == null ) || (reviews. size ( ) == 0 ) ) {
  19.                 empty. setText ( "No Data" ) ;
  20.             } else {
  21.                 reviewAdapter = new ReviewAdapter (ReviewList. this, reviews ) ;
  22.                 setListAdapter (reviewAdapter ) ;
  23.             }
  24.         }
  25.     } ;  
  26.  
  27.     @Override
  28.     public void onCreate (Bundle savedInstanceState ) {
  29.         super. onCreate (savedInstanceState ) ;
  30.         Log. 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.         listView. setItemsCanFocus ( false ) ;
  42.         listView. setChoiceMode ( ListView. CHOICE_MODE_SINGLE ) ;
  43.         listView. setEmptyView ( this. empty ) ;
  44.     }  
  45.  
  46.     @Override
  47.     protected void onResume ( ) {
  48.         super. onResume ( ) ;
  49.         Log. v (Constants. LOGTAG, " " + ReviewList. CLASSTAG + " onResume" ) ;
  50.         // get the current review criteria from the Application (global state placed there)
  51.         RestaurantFinderApplication application = (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.         loadReviews (criteriaLocation, criteriaCuisine, startFrom ) ;
  59.     }    
  60.    
  61.     @Override
  62.     public boolean onCreateOptionsMenu ( Menu menu ) {
  63.         super. onCreateOptionsMenu (menu ) ;
  64.         menu. add ( 0, ReviewList. MENU_GET_NEXT_PAGE, 0, R. string. menu_get_next_page ). setIcon (
  65.             android. R. drawable. ic_menu_more ) ;
  66.         menu. add ( 0, ReviewList. MENU_CHANGE_CRITERIA, 0, R. string. menu_change_criteria ). setIcon (
  67.             android. R. drawable. ic_menu_edit ) ;
  68.         return true ;
  69.     }    
  70.  
  71.     @Override
  72.     public boolean onMenuItemSelected ( int featureId, MenuItem item ) {
  73.         Intent intent = null ;
  74.         switch (item. getItemId ( ) ) {
  75.             case MENU_GET_NEXT_PAGE:
  76.                 // increment the startFrom value and call this Activity again
  77.                 intent = new Intent (Constants. INTENT_ACTION_VIEW_LIST ) ;
  78.                 intent. putExtra (Constants. STARTFROM_EXTRA, getIntent ( ). getIntExtra (Constants. STARTFROM_EXTRA, 1 )
  79.                     + ReviewList. NUM_RESULTS_PER_PAGE ) ;
  80.                 startActivity (intent ) ;
  81.                 return true ;
  82.             case MENU_CHANGE_CRITERIA:
  83.                 intent = new Intent ( this, ReviewCriteria. class ) ;
  84.                 startActivity (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 application = (RestaurantFinderApplication ) getApplication ( ) ;
  94.         application. setCurrentReview ( this. reviews. get (position ) ) ;
  95.  
  96.         // startFrom page is not stored in application, for example purposes it's a simple "extra"
  97.         Intent intent = new Intent (Constants. INTENT_ACTION_VIEW_DETAIL ) ;
  98.         intent. putExtra (Constants. STARTFROM_EXTRA, getIntent ( ). getIntExtra (Constants. STARTFROM_EXTRA, 1 ) ) ;
  99.         startActivity (intent ) ;
  100.     }    
  101.    
  102.     private void loadReviews ( String location, String cuisine, int startFrom ) {
  103.  
  104.         Log. v (Constants. LOGTAG, " " + ReviewList. CLASSTAG + " loadReviews" ) ;
  105.  
  106.         final ReviewFetcher rf = new ReviewFetcher (location, cuisine, "ALL", startFrom,
  107.             ReviewList. 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.                 reviews = rf. getReviews ( ) ;
  117.                 handler. sendEmptyMessage ( 0 ) ;
  118.             }
  119.         }. start ( ) ;
  120.     }
  121. }

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值