当前位置: 代码迷 >> Android >> Android讯息机制——Handler、Looper、MessageQueue
  详细解决方案

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

热度:146   发布时间:2016-05-01 18:43:03.0
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来交换信息

  相关解决方案