Android消息机制之Looper、Handler、MessageQueen

た 入场券 2023-06-23 07:00 16阅读 0赞

前言

Android消息机制可以说是我们Android工程师面试题中的必考题,弄懂它的原理是我们避不开的任务,所以长痛不如短痛,花点时间干掉他,废话不多说,开车啦

Android消息机制的简介

在安卓开发中,常常会遇到获取数据后更新UI的问题,比如:在获取网络信息后,需要弹出一个Toast

  1. HttpUtils.doGet("https://www.so.com/", new Callback() {
  2. @Override
  3. public void onFailure(Call call, IOException e) {
  4. }
  5. @Override
  6. public void onResponse(Call call, Response response) throws IOException {
  7. Toast.makeText(OkHttpActivity.this,"",Toast.LENGTH_SHORT).show();
  8. }
  9. });
  10. 1
  11. 2
  12. 3
  13. 4
  14. 5
  15. 6
  16. 7
  17. 8
  18. 9
  19. 10
  20. 11

这个时候程序就会报以下的错误

  1. java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
  2. 1

这是因为Android规定了只允许UI线程修改Activity里的UI组件,而我们刚才的操作在子线程中修改Activity里的UI组件,才会导致UI操作的线程不安全,并报出错误。为了保证Android的UI操作是线程安全的,Android提供了Handler消息传递机制来解决这个问题

Android消息机制的使用

在获取网络信息后,需要弹出一个Toast,正确做法是

  1. private static final int MAKE_TOAST = 0x01;
  2. private Handler mHandler = new Handler(){
  3. @Override
  4. public void handleMessage(Message msg) {
  5. switch (msg.what){
  6. case MAKE_TOAST:
  7. Toast.makeText(OkHttpActivity.this,"",Toast.LENGTH_SHORT).show();
  8. break;
  9. }
  10. }
  11. };
  12. @Override
  13. protected void onCreate(Bundle savedInstanceState) {
  14. super.onCreate(savedInstanceState);
  15. setContentView(R.layout.activity_gallery);
  16. HttpUtils.doGet("https://www.so.com/", new Callback() {
  17. @Override
  18. public void onFailure(Call call, IOException e) {
  19. }
  20. @Override
  21. public void onResponse(Call call, Response response) throws IOException {
  22. mHandler.sendEmptyMessageDelayed(MAKE_TOAST,200);
  23. }
  24. });
  25. }
  26. 1
  27. 2
  28. 3
  29. 4
  30. 5
  31. 6
  32. 7
  33. 8
  34. 9
  35. 10
  36. 11
  37. 12
  38. 13
  39. 14
  40. 15
  41. 16
  42. 17
  43. 18
  44. 19
  45. 20
  46. 21
  47. 22
  48. 23
  49. 24
  50. 25
  51. 26
  52. 27
  53. 28
  54. 29
  55. 30

在子线程中通过Handler发送消息,该消息会在Hanlder中的handleMessage()中被解析,并进行相对应的UI组件更新

Android消息机制的相关概念

一、相关概念的解释

1、主线程(UI线程)

  1. 定义:当程序第一次启动时,Android会同时启动一条主线程(Main Thread)
  2. 作用:主线程主要负责处理与UI相关的事件

2、Message(消息)

  1. 定义:Handler接收和处理的消息对象(Bean对象)
  2. 作用:通信时相关信息的存放和传递

3、ThreadLocal

  1. 定义:线程内部的数据存储类
  2. 作用:负责存储和获取本线程的Looper

4、Message Queue(消息队列)

  1. 定义:采用单链表的数据结构来存储消息列表
  2. 作用:用来存放通过Handler发过来的Message,按照先进先出执行

5、Handler(处理者)

  1. 定义:Message的主要处理者
  2. 作用:负责发送Message到消息队列&处理Looper分派过来的Message

6、Looper(循环器)

  1. 定义:扮演Message Queue和Handler之间桥梁的角色
  2. 作用:

    • 消息循环:循环取出Message Queue的Message
    • 消息派发:将取出的Message交付给相应的Handler

二、图片解读它们之间的关系

20161129232344072

三、文字解读它们之间的关系

Looper中存放有MessageQueen,MessageQueen中又有很多Message,当我们的Handler发送消息的时候,会获取当前的Looper,并在当前的Looper的MessageQueen当中存放我们发送的消息,而我们的MessageQueen也会在Looper的带动下,一直循环的读取Message信息,并将Message信息发送给Handler,并执行HandlerMessage()方法

其实这是一个循环的过程,读懂这句话和看懂图解很重要,会给我们下面的源码分析带来很大的帮助,所以建议大家先读懂前面的内容

Android消息机制的通信流程

这里采用网上的一张图,个人感觉图片概括得很好,就没必要再去造同样的轮子了,在新窗口打开可浏览大图

20161129184124711

Looper源码分析

一、根据上面的例子,为什么Handler可以在主线程中直接可以使用呢?

因为主线程(UI线程)的Looper在应用程序开启时创建好了,即在ActivityThread.main方法中创建的,该函数为Android应用程序的入口

  1. public static void main(String[] args) {
  2. ...
  3. Process.setArgV0("<pre-initialized>");
  4. //1. 创建消息循环Looper
  5. Looper.prepareMainLooper();
  6. ActivityThread thread = new ActivityThread();
  7. thread.attach(false);
  8. if (sMainThreadHandler == null) {
  9. sMainThreadHandler = thread.getHandler();
  10. }
  11. ...
  12. //2. 执行消息循环
  13. Looper.loop();
  14. throw new RuntimeException("Main thread loop unexpectedly exited");
  15. }
  16. 1
  17. 2
  18. 3
  19. 4
  20. 5
  21. 6
  22. 7
  23. 8
  24. 9
  25. 10
  26. 11
  27. 12
  28. 13
  29. 14
  30. 15
  31. 16
  32. 17

Looper中最为重要的两个方法:

  • Looper.prepareMainLooper():该方法是Looper对象的初始化
  • Looper.loop():该方法会循环取出Message Queue的Message,将取出的Message交付给相应的Handler(Looper的作用就体现在这里)

二、Looper.prepareMainLooper()

  1. //在主线程中初始化Looper
  2. public static void prepareMainLooper() {
  3. //在这里会调用prepare(boolean quitAllowed)方法
  4. prepare(false);
  5. synchronized (Looper.class) {
  6. if (sMainLooper != null) {
  7. throw new IllegalStateException("The main Looper has already been prepared.");
  8. }
  9. sMainLooper = myLooper();
  10. }
  11. }
  12. //看下prepare(boolean quitAllowed)方法
  13. private static void prepare(boolean quitAllowed) {
  14. //判断sThreadLocal是否为null,否则抛出异常
  15. //即Looper.prepare()方法不能被调用两次
  16. //也就是说,一个线程中只能对应一个Looper实例
  17. if (sThreadLocal.get() != null) {
  18. throw new RuntimeException("Only one Looper may be created per thread");
  19. }
  20. //初始化Looper对象设置到ThreadLocal中
  21. sThreadLocal.set(new Looper(quitAllowed));
  22. }
  23. //看下Looper的构造方法
  24. private Looper(boolean quitAllowed) {
  25. //创建了一个MessageQueue(消息队列)
  26. //这说明,当创建一个Looper实例时,会自动创建一个与之配对的MessageQueue(消息队列)
  27. mQueue = new MessageQueue(quitAllowed);
  28. mThread = Thread.currentThread();
  29. }
  30. 1
  31. 2
  32. 3
  33. 4
  34. 5
  35. 6
  36. 7
  37. 8
  38. 9
  39. 10
  40. 11
  41. 12
  42. 13
  43. 14
  44. 15
  45. 16
  46. 17
  47. 18
  48. 19
  49. 20
  50. 21
  51. 22
  52. 23
  53. 24
  54. 25
  55. 26
  56. 27
  57. 28
  58. 29
  59. 30
  60. 31

整个Looper的初始化准备工作就完了,这里做了哪几件事:

  • Looper的创建会关联一个MessageQueen的创建
  • Looper对象只能被创建一次
  • Looper对象创建后被存放在sThreadLocal中

三、Looper.loop()

  1. public static void loop() {
  2. //myLooper()方法作用是返回sThreadLocal存储的Looper实例,如果me为null,loop()则抛出异常
  3. //也就是说loop方法的执行必须在prepare方法之后运行
  4. //也就是说,消息循环必须要先在线程当中创建Looper实例
  5. final Looper me = myLooper();
  6. if (me == null) {
  7. throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
  8. }
  9. //获取looper实例中的mQueue(消息队列)
  10. final MessageQueue queue = me.mQueue;
  11. Binder.clearCallingIdentity();
  12. final long ident = Binder.clearCallingIdentity();
  13. //进入消息循环
  14. for (;;) {
  15. //next()方法用于取出消息队列里的消息
  16. //如果取出的消息为空,则线程阻塞
  17. Message msg = queue.next();
  18. if (msg == null) {
  19. return;
  20. }
  21. final Printer logging = me.mLogging;
  22. if (logging != null) {
  23. logging.println(">>>>> Dispatching to " + msg.target + " " +
  24. msg.callback + ": " + msg.what);
  25. }
  26. final long traceTag = me.mTraceTag;
  27. if (traceTag != 0) {
  28. Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
  29. }
  30. try {
  31. //消息派发:把消息派发给msg的target属性,然后用dispatchMessage方法去处理
  32. //Msg的target其实就是handler对象,下面会继续分析
  33. msg.target.dispatchMessage(msg);
  34. } finally {
  35. if (traceTag != 0) {
  36. Trace.traceEnd(traceTag);
  37. }
  38. }
  39. if (logging != null) {
  40. logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
  41. }
  42. final long newIdent = Binder.clearCallingIdentity();
  43. if (ident != newIdent) {
  44. Log.wtf(TAG, "Thread identity changed from 0x"
  45. + Long.toHexString(ident) + " to 0x"
  46. + Long.toHexString(newIdent) + " while dispatching to "
  47. + msg.target.getClass().getName() + " "
  48. + msg.callback + " what=" + msg.what);
  49. }
  50. //释放消息占据的资源
  51. msg.recycleUnchecked();
  52. }
  53. }
  54. 1
  55. 2
  56. 3
  57. 4
  58. 5
  59. 6
  60. 7
  61. 8
  62. 9
  63. 10
  64. 11
  65. 12
  66. 13
  67. 14
  68. 15
  69. 16
  70. 17
  71. 18
  72. 19
  73. 20
  74. 21
  75. 22
  76. 23
  77. 24
  78. 25
  79. 26
  80. 27
  81. 28
  82. 29
  83. 30
  84. 31
  85. 32
  86. 33
  87. 34
  88. 35
  89. 36
  90. 37
  91. 38
  92. 39
  93. 40
  94. 41
  95. 42
  96. 43
  97. 44
  98. 45
  99. 46
  100. 47
  101. 48
  102. 49
  103. 50
  104. 51
  105. 52
  106. 53
  107. 54
  108. 55
  109. 56
  110. 57
  111. 58
  112. 59

整个Looper的循环过程就完了,这里做了哪几件事:

  • 取出Looper和MessageQueen
  • 进入消息循环,有消息则分发出去
  • 消息资源的回收

四、Looper的退出

当然Looper也提供了两个方法可以退出一个Looper:

  • quit():quit会直接退出Looper
  • quitSafety():quitSafety只是设定一个退出标记,然后把消息队列中的已有消息处理完毕后退出Looper

MessageQueen源码分析

一、由于MessageQueen是用来存放Message的,那么是如何存储Message的呢?

由于Handler使用Post()方法将Message传递到MessageQueen中,在MessageQueen中会使用enqueueMessage()方法存储Message,其实现的方式是通过单链表的数据结构来存储消息列表

  1. boolean enqueueMessage(Message msg, long when) {
  2. ...
  3. synchronized (this) {
  4. ...
  5. msg.markInUse();
  6. msg.when = when;
  7. Message p = mMessages;
  8. boolean needWake;
  9. if (p == null || when == 0 || when < p.when) {
  10. msg.next = p;
  11. mMessages = msg;
  12. needWake = mBlocked;
  13. } else {
  14. needWake = mBlocked && p.target == null && msg.isAsynchronous();
  15. Message prev;
  16. for (;;) {
  17. prev = p;
  18. p = p.next;
  19. if (p == null || when < p.when) {
  20. break;
  21. }
  22. if (needWake && p.isAsynchronous()) {
  23. needWake = false;
  24. }
  25. }
  26. msg.next = p;
  27. prev.next = msg;
  28. }
  29. if (needWake) {
  30. nativeWake(mPtr);
  31. }
  32. }
  33. return true;
  34. }
  35. 1
  36. 2
  37. 3
  38. 4
  39. 5
  40. 6
  41. 7
  42. 8
  43. 9
  44. 10
  45. 11
  46. 12
  47. 13
  48. 14
  49. 15
  50. 16
  51. 17
  52. 18
  53. 19
  54. 20
  55. 21
  56. 22
  57. 23
  58. 24
  59. 25
  60. 26
  61. 27
  62. 28
  63. 29
  64. 30
  65. 31
  66. 32
  67. 33
  68. 34
  69. 35

整个进队列的过程就完了,这里做了哪几件事:

  • 首先判断消息队列里有没有消息,没有的话则将当前插入的消息作为队头,并且这时消息队列如果处于等待状态的话则将其唤醒
  • 若是在中间插入,则根据Message创建的时间进行插入

二、既然MessageQueen存了消息之后,是如何提供取出来的方法的呢?

我们知道存消息是Handler存进来的,那么取消息就应该是Looper中取了,从Looper的源码可以看出,消息就是在Looper中取出的,其实现是用MessageQueen里面的next()方法

  1. Message next() {
  2. ......
  3. int nextPollTimeoutMillis = 0;
  4. for (;;) {
  5. if (nextPollTimeoutMillis != 0) {
  6. Binder.flushPendingCommands();
  7. }
  8. // nativePollOnce方法在native层,若是nextPollTimeoutMillis为-1,这时候消息队列处于等待状态。   
  9. nativePollOnce(ptr, nextPollTimeoutMillis);
  10. synchronized (this) {
  11. final long now = SystemClock.uptimeMillis();
  12. Message prevMsg = null;
  13. Message msg = mMessages;
  14. if (msg != null && msg.target == null) {
  15. do {
  16. prevMsg = msg;
  17. msg = msg.next;
  18. } while (msg != null && !msg.isAsynchronous());
  19. }
  20. //按照我们设置的时间取出消息
  21. if (msg != null) {
  22. if (now < msg.when) {
  23. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
  24. } else {
  25. mBlocked = false;
  26. if (prevMsg != null) {
  27. prevMsg.next = msg.next;
  28. } else {
  29. mMessages = msg.next;
  30. }
  31. msg.next = null;
  32. if (DEBUG) Log.v(TAG, "Returning message: " + msg);
  33. msg.markInUse();
  34. return msg;
  35. }
  36. } else {
  37. // 如果消息队列中没有消息,将nextPollTimeoutMillis设为-1,下次循环消息队列则处于等待状态
  38. nextPollTimeoutMillis = -1;
  39. }
  40. //退出消息队列,返回null,这时候Looper中的消息循环也会终止。
  41. if (mQuitting) {
  42. dispose();
  43. return null;
  44. }
  45. ......
  46. }
  47. .....
  48. }
  49. }
  50. 1
  51. 2
  52. 3
  53. 4
  54. 5
  55. 6
  56. 7
  57. 8
  58. 9
  59. 10
  60. 11
  61. 12
  62. 13
  63. 14
  64. 15
  65. 16
  66. 17
  67. 18
  68. 19
  69. 20
  70. 21
  71. 22
  72. 23
  73. 24
  74. 25
  75. 26
  76. 27
  77. 28
  78. 29
  79. 30
  80. 31
  81. 32
  82. 33
  83. 34
  84. 35
  85. 36
  86. 37
  87. 38
  88. 39
  89. 40
  90. 41
  91. 42
  92. 43
  93. 44
  94. 45
  95. 46
  96. 47
  97. 48
  98. 49
  99. 50
  100. 51
  101. 52

三、在MessageQueen存消息的媒介当然是通过Message对象啦,那这个Message对象又是什么呢?

其实这个Message就是用来存储Message中各种信息的Bean对象,从源码中可以其属性,这里例举我们常用的几个

  1. public int what;
  2. public int arg1;
  3. public int arg2;
  4. public Object obj;
  5. public Messenger replyTo;
  6. int flags;
  7. long when;
  8. Bundle data;
  9. Handler target;
  10. Runnable callback;
  11. 1
  12. 2
  13. 3
  14. 4
  15. 5
  16. 6
  17. 7
  18. 8
  19. 9
  20. 10

Handler源码分析

一、Handler的创建

Handler的创建会关联一个Looper对象,而Looper对象是关联着MessageQueen对象,所以在Handler创建时候,取出Looper和MessageQueen

  1. public Handler(Callback callback, boolean async) {
  2. ...
  3. //取出Looper
  4. mLooper = Looper.myLooper();
  5. if (mLooper == null) {
  6. throw new RuntimeException(
  7. "Can't create handler inside thread that has not called Looper.prepare()");
  8. }
  9. //取出Looper中的MessageQueen
  10. mQueue = mLooper.mQueue;
  11. mCallback = callback;
  12. mAsynchronous = async;
  13. }
  14. 1
  15. 2
  16. 3
  17. 4
  18. 5
  19. 6
  20. 7
  21. 8
  22. 9
  23. 10
  24. 11
  25. 12
  26. 13

前面我们也说过了Looper是存放在ThreadLocal里面的,可以看到下面的源码就知道了

  1. public static @Nullable Looper myLooper() {
  2. return sThreadLocal.get();
  3. }
  4. 1
  5. 2
  6. 3

整个创建的过程就完了,这里做了哪几件事:

  • 取出Looper
  • 取出Looper中的MessageQueen

二、Handler发送消息

1、方式一:sendMessage(Message msg)

  1. public final boolean sendEmptyMessage(int what)
  2. {
  3. return sendEmptyMessageDelayed(what, 0);
  4. }
  5. //往下追踪
  6. public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
  7. Message msg = Message.obtain();
  8. msg.what = what;
  9. return sendMessageDelayed(msg, delayMillis);
  10. }
  11. //往下追踪
  12. public final boolean sendMessageDelayed(Message msg, long delayMillis)
  13. {
  14. if (delayMillis < 0) {
  15. delayMillis = 0;
  16. }
  17. return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
  18. }
  19. //往下追踪
  20. public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
  21. //直接获取MessageQueue
  22. MessageQueue queue = mQueue;
  23. if (queue == null) {
  24. RuntimeException e = new RuntimeException(
  25. this + " sendMessageAtTime() called with no mQueue");
  26. Log.w("Looper", e.getMessage(), e);
  27. return false;
  28. }
  29. return enqueueMessage(queue, msg, uptimeMillis);
  30. }
  31. //调用sendMessage方法其实最后是调用了enqueueMessage方法
  32. private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
  33. //为msg.target赋值为this,也就是把当前的handler作为msg的target属性
  34. //如果大家还记得Looper的loop()方法会取出每个msg然后执行msg.target.dispatchMessage(msg)去处理消息,其实就是派发给相应的Handler
  35. msg.target = this;
  36. if (mAsynchronous) {
  37. msg.setAsynchronous(true);
  38. }
  39. //最终调用queue的enqueueMessage的方法,也就是说handler发出的消息,最终会保存到消息队列中去
  40. return queue.enqueueMessage(msg, uptimeMillis);
  41. }
  42. 1
  43. 2
  44. 3
  45. 4
  46. 5
  47. 6
  48. 7
  49. 8
  50. 9
  51. 10
  52. 11
  53. 12
  54. 13
  55. 14
  56. 15
  57. 16
  58. 17
  59. 18
  60. 19
  61. 20
  62. 21
  63. 22
  64. 23
  65. 24
  66. 25
  67. 26
  68. 27
  69. 28
  70. 29
  71. 30
  72. 31
  73. 32
  74. 33
  75. 34
  76. 35
  77. 36
  78. 37
  79. 38
  80. 39
  81. 40
  82. 41
  83. 42
  84. 43
  85. 44
  86. 45

2、方式二:post(Ruunable r)

  1. public final boolean post(Runnable r)
  2. {
  3. return sendMessageDelayed(getPostMessage(r), 0);
  4. }
  5. 1
  6. 2
  7. 3
  8. 4

其实post()方法最终也会保存到消息队列中去,和上面不同的是它传进来的一个Runnable对象,执行了getPostMessage()方法,我们往下追踪

  1. private static Message getPostMessage(Runnable r) {
  2. Message m = Message.obtain();
  3. m.callback = r;
  4. return m;
  5. }
  6. 1
  7. 2
  8. 3
  9. 4
  10. 5

实质上就是将这个Runnable保存在Message的变量中,这就导致了我们下面处理消息的时候有两种不同方案

三、Handler处理消息

你还记得前面所说Looper中msg.target.dispatchMessage()方法吗?这个方法就是调用Handler的dispatchMessage()

  1. public void dispatchMessage(Message msg) {
  2. if (msg.callback != null) {
  3. //1. post()方法的处理方法
  4. handleCallback(msg);
  5. } else {
  6. if (mCallback != null) {
  7. if (mCallback.handleMessage(msg)) {
  8. return;
  9. }
  10. }
  11. //2. sendMessage()方法的处理方法
  12. handleMessage(msg);
  13. }
  14. }
  15. //1. post()方法的最终处理方法
  16. private static void handleCallback(Message message) {
  17. message.callback.run();
  18. }
  19. //2. sendMessage()方法的最终处理方法
  20. public void handleMessage(Message msg) {
  21. }
  22. 1
  23. 2
  24. 3
  25. 4
  26. 5
  27. 6
  28. 7
  29. 8
  30. 9
  31. 10
  32. 11
  33. 12
  34. 13
  35. 14
  36. 15
  37. 16
  38. 17
  39. 18
  40. 19
  41. 20
  42. 21
  43. 22
  44. 23

整个处理的过程就完了,这里做了哪几件事:

  • post()方法的处理方法就是将传进来的Runnable执行run()方法
  • sendMessage()方法的处理方法就是执行handleMessage()空方法,这也是我们为什么要在Handler重写这个方法的原因

面试题

一、请解释下Android通信机制中Message、Handler、MessageQueen、Looper的之间的关系?

首先,是这个MessageQueen,MessageQueen是一个消息队列,它可以存储Handler发送过来的消息,其内部提供了进队和出队的方法来管理这个消息队列,其出队和进队的原理是采用单链表的数据结构进行插入和删除的,即enqueueMessage()方法和next()方法。这里提到的Message,其实就是一个Bean对象,里面的属性用来记录Message的各种信息。

然后,是这个Looper,Looper是一个循环器,它可以循环的取出MessageQueen中的Message,其内部提供了Looper的初始化和循环出去Message的方法,即prepare()方法和loop()方法。在prepare()方法中,Looper会关联一个MessageQueen,而且将Looper存进一个ThreadLocal中,在loop()方法中,通过ThreadLocal取出Looper,使用MessageQueen的next()方法取出Message后,判断Message是否为空,如果是则Looper阻塞,如果不是,则通过dispatchMessage()方法分发该Message到Handler中,而Handler执行handlerMessage()方法,由于handlerMessage()方法是个空方法,这也是为什么需要在Handler中重写handlerMessage()方法的原因。这里要注意的是Looper只能在一个线程中只能存在一个。这里提到的ThreadLocal,其实就是一个对象,用来在不同线程中存放对应线程的Looper。

最后,是这个Handler,Handler是Looper和MessageQueen的桥梁,Handler内部提供了发送Message的一系列方法,最终会通过MessageQueen的enqueueMessage()方法将Message存进MessageQueen中。我们平时可以直接在主线程中使用Handler,那是因为在应用程序启动时,在入口的main方法中已经默认为我们创建好了Looper。

二、Handler能在子线程创建吗?

Handler是不可以直接在子线程创建的,由于Handler在子线程没有Looper,所以在子线程中会报错。但是可以通过间接的方式在子线程中创建Handler,有两种方式可以在子线程中创建Handler

方式一:在子线程创建Handler的时候使用主线程的Looper,这样handlerMessage的回调就在主线程运行

  1. new Thread(new Runnable() {
  2. public void run() {
  3. // Looper.getMainLooper()获取主线程的Looper
  4. Handler handler = new Handler(Looper.getMainLooper()){
  5. @Override
  6. public void handleMessage(Message msg) {
  7. // 在主线程中执行,可以做UI操作
  8. Toast.makeText(getApplicationContext(), "handler msg", Toast.LENGTH_LONG).show();
  9. }
  10. };
  11. handler.sendEmptyMessage(1);
  12. };
  13. }).start();
  14. 1
  15. 2
  16. 3
  17. 4
  18. 5
  19. 6
  20. 7
  21. 8
  22. 9
  23. 10
  24. 11
  25. 12
  26. 13

方式二:在子线程创建Handler的时候创建子线程的Looper,这样handlerMessage的回调就在子线程运行

  1. new Thread(new Runnable() {
  2. public void run() {
  3. // 创建子线程Looper
  4. Looper.prepare();
  5. Handler handler = new Handler(){
  6. @Override
  7. public void handleMessage(Message msg) {
  8. // 在子线程中执行,不可以做UI操作
  9. Toast.makeText(getApplicationContext(), "handler msg", Toast.LENGTH_LONG).show();
  10. }
  11. };
  12. // 开始循环Looper
  13. Looper.loop();
  14. };
  15. }).start();
  16. 1
  17. 2
  18. 3
  19. 4
  20. 5
  21. 6
  22. 7
  23. 8
  24. 9
  25. 10
  26. 11
  27. 12
  28. 13
  29. 14
  30. 15

易错点:在子线程去创建对象,对象本身又实例化Handler,问题又会回到子线程创建Handler导致报错的问题

  1. public class MainActivity extends AppCompatActivity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.activity_main);
  6. new Thread(new Runnable() {
  7. @Override
  8. public void run() {
  9. // 子线程实例化对象
  10. Stub stub = new Stub();
  11. stub.startAction();
  12. }
  13. }).start();
  14. }
  15. }
  16. public class Stub {
  17. // 在子线程中创建Handler,会报错
  18. public Handler mHanlder = new Handler(){
  19. @Override
  20. public void handleMessage(Message msg) {
  21. super.handleMessage(msg);
  22. }
  23. };
  24. public void startAction(){
  25. }
  26. }
  27. 1
  28. 2
  29. 3
  30. 4
  31. 5
  32. 6
  33. 7
  34. 8
  35. 9
  36. 10
  37. 11
  38. 12
  39. 13
  40. 14
  41. 15
  42. 16
  43. 17
  44. 18
  45. 19
  46. 20
  47. 21
  48. 22
  49. 23
  50. 24
  51. 25
  52. 26
  53. 27
  54. 28
  55. 29
  56. 30
  57. 31
  58. 32

结语

学习完了消息机制,回过头来看还是挺简单的。由于每个人都有惧怕困难的天性,一开始我也是看不懂,很怕看源码,但是我还是坚持了2天时间,借助书本知识和网上的知识,将这个消息机制给克服了。慢慢的,我们会将养成这种习惯,那么你离大神的脚步就不远了,加油吧

发表评论

表情:
评论列表 (有 0 条评论,16人围观)

还没有评论,来说两句吧...

相关阅读