`
shuai1234
  • 浏览: 926630 次
  • 性别: Icon_minigender_1
  • 来自: 山西
社区版块
存档分类
最新评论

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来交换信息

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics