最近在做毕设,关于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. public Handler (Looper looper ) {
  11. mLooper = looper ;
  12. mQueue = looper. mQueue ;
  13. mCallback = null ;
  14. }

从这两个构造函数中,我们发现当为提供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. public void run ( ) {
  4. Looper. prepare ( ) ;
  5. mHandler = new Handler ( ) {
  6. public void handleMessage (Message msg ) {
  7. // process incoming messages here
  8. }
  9. } ;
  10. Looper. loop ( ) ;
  11. }

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

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

更多相关文章

  1. Android的线程模型
  2. Android 消息提示框:五种Toast详解
  3. Android 消息机制之Message
  4. Android的消息处理机制(深入源码)
  5. Android 多线程AsyncTask详解
  6. android 上 webkit js 扩展之全局本地对象实现步骤
  7. Android消息处理系统原理简要概述
  8. Android 多线程之 AsyncTask
  9. android主线程和子线程的区别

随机推荐

  1. php连接mssql两种方法(com &amp; pdo)
  2. 本机PHP函数将授予我直接访问字符串部分
  3. php多进程处理
  4. 基于MySQL查询的Laravel路由
  5. 【实战】如何通过html+css+mysql+php来快
  6. 在使用PHP编程时,使用存储过程还是硬编码S
  7. 如何在php / mysql中使用事务
  8. Zend Studio-8.0.0中文汉化教程及入门教
  9. 使用php codeigniter进行Mysql数据库同步
  10. 填充PHP数组:首先检查索引?