飞道的博客

Android核心原理源码解析

381人阅读  评论(0)

Handler

放入消息主要函数

Handler工作流程

1. 子线程调用Handler的boolean sendMessage()方法,最终调用MessageQueue中的boolean enqueueMessage(Message msg, long when) 方法,将msg放入到MessageQueue消息队列中

2. Loop调用public static void loop() 方法,loop()方法会执行一个死循环,不断的调用Message msg = queue.next();方法获取消息,获取到消息后,通过msg.target.dispatchMessage(msg);方法派遣消息,然后dispatchMessage这个方法最终会调用Handler的handlerMessage方法执行我们自己写的逻辑


  
  1. public final class Looper {
  2. public static void loop() {
  3. final Looper me = myLooper();
  4. ...
  5. final MessageQueue queue = me.mQueue;
  6. ...
  7. for (;;) {
  8. //相当于获取消息
  9. Message msg = queue.next(); // might block
  10. ...
  11. try {
  12. //获取消息后,派遣消息
  13. msg.target.dispatchMessage(msg);
  14. ...
  15. }
  16. ...
  17. }
  18. }
  19. }
  20. public class Handler {
  21. public void dispatchMessage(@NonNull Message msg) {
  22. if (msg.callback != null) {
  23. handleCallback(msg);
  24. } else {
  25. if (mCallback != null) {
  26. if (mCallback.handleMessage(msg)) {
  27. return;
  28. }
  29. }
  30. //触发handleMessage处理消息,需要自己实现的方法
  31. handleMessage(msg);
  32. }
  33. }
  34. }

一个线程只有一个Loop,原理?

应用开始启动的时候,首先会调用ActivityThread的main方法,main方法会执行一个Looper.prepareMainLooper();初始化Looper

Looper能启动的原因,是因为loop是因为main方法中调用的loop方法,这个方法是一个死循环,这个死循环会不断的轮询msg

1. 如何初始化Looper的?

main方法中调用Looper.prepareMainLooper();


  
  1. public final class Looper {
  2. private static Looper sMainLooper; //主Looper
  3. static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
  4. @Deprecated
  5. public static void prepareMainLooper() {
  6. prepare( false); //向ThreadLocal中初始化一个Looper
  7. synchronized (Looper.class) {
  8. if (sMainLooper != null) {//保证了一个线程只能创建一个 Looper
  9. throw new IllegalStateException("The main Looper has already been prepared.");
  10. }
  11. sMainLooper = myLooper(); //sMainLooper的值从ThreadLocal中获取
  12. }
  13. }
  14. private static void prepare(boolean quitAllowed) {
  15. if (sThreadLocal.get() != null) {
  16. throw new RuntimeException( "Only one Looper may be created per thread");
  17. }
  18. sThreadLocal.set( new Looper(quitAllowed));
  19. }
  20. private Looper(boolean quitAllowed) {
  21. mQueue = new MessageQueue(quitAllowed);
  22. mThread = Thread.currentThread();
  23. }
  24. public static @Nullable Looper myLooper() {
  25. return sThreadLocal.get();
  26. }
  27. }

2.Handler内存泄漏

1. 原因

Handler在调用sendMessage的时候,这个方法会调用到Handler的enqueueMessage方法,这个方法会将Handler赋值给msg的target

MessageQueue持有msg,这时msg对象会持有Handler的引用,Handler中有 this Activity,那么this Activity中大量内存都不会释放,就会造成大量的内存泄漏

如果是一个延迟消息,那么这个消息会一直在MessageQueue中,msg-Handler-this Activity ,那么内存就一直没办法回收


  
  1. public class Handler {
  2. private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
  3. long uptimeMillis) {
  4. msg.target = this;
  5. msg.workSourceUid = ThreadLocalWorkSource.getUid();
  6. if (mAsynchronous) {
  7. msg.setAsynchronous( true);
  8. }
  9. return queue.enqueueMessage(msg, uptimeMillis);
  10. }
  11. }

2. 解决办法

①将handler声明为静态类,②用软引用或弱引用来持有activity

③在destroy方法里面清理handler中的所有Message

3. 为什么主线程可以new handler?如何在子线程中new handler

①为什么主线程中可以new Handler

在应用启动的时候,Looper就被初始化好了,在ActivityThread调用Looper.prepareMainLooper();//初始化Looper,在调用Handler的构造器时,会将Looper传给Handler的Looper,构造器没有添加Looper,默认添加主线程的Looper

new Looper源码


  
  1. public class Handler {
  2. private static final boolean FIND_POTENTIAL_LEAKS = false;
  3. private static final String TAG = "Handler";
  4. private static Handler MAIN_THREAD_HANDLER = null;
  5. ...
  6. @UnsupportedAppUsage
  7. final Looper mLooper;
  8. final MessageQueue mQueue;
  9. @UnsupportedAppUsage
  10. final Callback mCallback;
  11. final boolean mAsynchronous;
  12. @UnsupportedAppUsage
  13. IMessenger mMessenger;
  14. ...
  15. @Deprecated
  16. public Handler() {
  17. this( null, false);
  18. }
  19. public Handler(@Nullable Callback callback, boolean async) {
  20. if (FIND_POTENTIAL_LEAKS) {
  21. final Class<? extends Handler> klass = getClass();
  22. if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
  23. (klass.getModifiers() & Modifier.STATIC) == 0) {
  24. Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
  25. klass.getCanonicalName());
  26. }
  27. }
  28. mLooper = Looper.myLooper();
  29. if (mLooper == null) {
  30. throw new RuntimeException(
  31. "Can't create handler inside thread " + Thread.currentThread()
  32. + " that has not called Looper.prepare()");
  33. }
  34. mQueue = mLooper.mQueue;
  35. mCallback = callback;
  36. mAsynchronous = async;
  37. }
  38. }

②如何在子线程中new Handler

用法


  
  1. HandlerThread handlerThread = new HandlerThread( "name");
  2. handlerThread.start();
  3. Handler handler = new Handler(handlerThread.getLooper()) {
  4. @Override
  5. public void handleMessage(@NonNull Message msg) {
  6. super.handleMessage(msg);
  7. //...自己实现的方法
  8. }
  9. };

上述代码对应源码解析


  
  1. public class HandlerThread extends Thread {
  2. int mPriority;
  3. int mTid = - 1;
  4. Looper mLooper;
  5. private @Nullable Handler mHandler;
  6. public HandlerThread(String name) {
  7. super(name);
  8. mPriority = Process.THREAD_PRIORITY_DEFAULT;
  9. }
  10. public HandlerThread(String name, int priority) {
  11. super(name);
  12. mPriority = priority;
  13. }
  14. protected void onLooperPrepared() {
  15. }
  16. @Override
  17. public void run() {
  18. mTid = Process.myTid();
  19. Looper.prepare();
  20. synchronized ( this) {
  21. mLooper = Looper.myLooper();
  22. notifyAll();
  23. }
  24. Process.setThreadPriority(mPriority);
  25. onLooperPrepared();
  26. Looper.loop();
  27. mTid = - 1;
  28. }
  29. public Looper getLooper() {
  30. if (!isAlive()) {
  31. return null;
  32. }
  33. // If the thread has been started, wait until the looper has been created.
  34. synchronized ( this) {
  35. while (isAlive() && mLooper == null) {
  36. try {
  37. wait();
  38. } catch (InterruptedException e) {
  39. }
  40. }
  41. }
  42. return mLooper;
  43. }
  44. @NonNull
  45. public Handler getThreadHandler() {
  46. if (mHandler == null) {
  47. mHandler = new Handler(getLooper());
  48. }
  49. return mHandler;
  50. }
  51. public boolean quit() {
  52. Looper looper = getLooper();
  53. if (looper != null) {
  54. looper.quit();
  55. return true;
  56. }
  57. return false;
  58. }
  59. public boolean quitSafely() {
  60. Looper looper = getLooper();
  61. if (looper != null) {
  62. looper.quitSafely();
  63. return true;
  64. }
  65. return false;
  66. }
  67. public int getThreadId() {
  68. return mTid;
  69. }
  70. }
  71. public class Handler {
  72. ...
  73. public Handler(@NonNull Looper looper) {
  74. this(looper, null, false);
  75. }
  76. @UnsupportedAppUsage
  77. public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
  78. mLooper = looper;
  79. mQueue = looper.mQueue;
  80. mCallback = callback;
  81. mAsynchronous = async;
  82. }
  83. ...
  84. }

4. 子线程中维护的Looper,消息队列中无消息的时候应该怎么处理?有什么作用?主线程呢?

①当消息队列中没有消息的时候,怎么处理

当消息队列中没有消息的时候,在调用next()函数的时候会休眠,应该调用Looper类的quitSafely()函数,让loop()函数退出;

②作用

当loop()函数退出,这时线程就会退出,线程所占用的资源就都会被释放

如果需要重新发送消息,这时重新创建一个子线程对应的Handler就好,重新创建Looper

Looper类对应源代码如下


  
  1. public final class Looper {
  2. ...
  3. public static void loop() {
  4. final Looper me = myLooper();
  5. ...
  6. for (;;) {
  7. //相当于获取消息
  8. Message msg = queue.next();
  9. if (msg == null) {
  10. return; //退出loop()函数
  11. }
  12. ...
  13. }
  14. }
  15. }
  16. public final class MessageQueue {
  17. ...
  18. @UnsupportedAppUsage
  19. Message next() {
  20. final long ptr = mPtr;
  21. if (ptr == 0) {
  22. return null;
  23. }
  24. int pendingIdleHandlerCount = - 1; // -1 only during first iteration
  25. int nextPollTimeoutMillis = 0;
  26. for (;;) {
  27. if (nextPollTimeoutMillis != 0) {
  28. Binder.flushPendingCommands();
  29. }
  30. nativePollOnce(ptr, nextPollTimeoutMillis); //睡眠
  31. synchronized ( this) {
  32. ...
  33. if (msg != null) {
  34. ...
  35. } else {
  36. nextPollTimeoutMillis = - 1;
  37. }
  38. if (mQuitting) {
  39. dispose();
  40. return null; //这里会退出死循环,返回null
  41. }
  42. ...
  43. }
  44. ...
  45. }
  46. ...
  47. }
  48. ...
  49. }

Looper类的quitSafety()方法对应源码


  
  1. public final class Looper {
  2. ...
  3. public void quitSafely() {
  4. mQueue.quit( true);
  5. }
  6. ...
  7. }
  8. public final class MessageQueue {
  9. ...
  10. void quit(boolean safe) {
  11. if (!mQuitAllowed) {
  12. throw new IllegalStateException( "Main thread not allowed to quit.");
  13. }
  14. synchronized ( this) {
  15. if (mQuitting) {
  16. return;
  17. }
  18. mQuitting = true;
  19. if (safe) {
  20. removeAllFutureMessagesLocked();
  21. } else {
  22. removeAllMessagesLocked();
  23. }
  24. //唤醒因为没有消息而睡眠的线程
  25. nativeWake(mPtr);
  26. }
  27. }
  28. ...
  29. private void removeAllMessagesLocked() {
  30. Message p = mMessages;
  31. while (p != null) {
  32. Message n = p.next;
  33. p.recycleUnchecked();
  34. p = n;
  35. }
  36. mMessages = null;
  37. }
  38. //移除掉所有的Message
  39. private void removeAllFutureMessagesLocked() {
  40. final long now = SystemClock.uptimeMillis();
  41. Message p = mMessages;
  42. if (p != null) {
  43. if (p.when > now) {
  44. removeAllMessagesLocked();
  45. } else {
  46. Message n;
  47. for (;;) {
  48. n = p.next;
  49. if (n == null) {
  50. return;
  51. }
  52. if (n.when > now) {
  53. break;
  54. }
  55. p = n;
  56. }
  57. p.next = null;
  58. do {
  59. p = n;
  60. n = p.next;
  61. p.recycleUnchecked();
  62. } while (n != null);
  63. }
  64. }
  65. }
  66. }

③主线程没有消息怎样处理

会一直休眠,必须不可以退出

5. 既然可以存在多个Handler往MessageQueue中添加数据(发消息时各个Handler可能处于不同线程),如何保证线程安全?取消息呢

①多个Handler发消息,如何保证线程安全?

Looper中的MessageQueue是唯一的,多个Handler中的MessageQueue是来自于Looper中的MessageQueue,线程和Looper一一对应,Looper和MessageQueue一一对应,每一个线程只有一个MessageQueue

而且向MessageQueue中添加消息需要加锁,以此来保证线程安全

对应源码


  
  1. public final class Looper {
  2. ...
  3. @UnsupportedAppUsage
  4. final MessageQueue mQueue;
  5. ...
  6. public static void prepare() {
  7. prepare( true);
  8. }
  9. private static void prepare(boolean quitAllowed) {
  10. if (sThreadLocal.get() != null) {
  11. throw new RuntimeException( "Only one Looper may be created per thread");
  12. }
  13. sThreadLocal.set( new Looper(quitAllowed));
  14. }
  15. ...
  16. private Looper(boolean quitAllowed) {
  17. mQueue = new MessageQueue(quitAllowed);
  18. mThread = Thread.currentThread();
  19. }
  20. }
  21. public class Handler {
  22. ...
  23. @UnsupportedAppUsage
  24. final Looper mLooper;
  25. final MessageQueue mQueue;
  26. @UnsupportedAppUsage
  27. final Callback mCallback;
  28. final boolean mAsynchronous;
  29. @UnsupportedAppUsage
  30. IMessenger mMessenger;
  31. ...
  32. @UnsupportedAppUsage
  33. public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
  34. mLooper = looper;
  35. mQueue = looper.mQueue;
  36. mCallback = callback;
  37. mAsynchronous = async;
  38. }
  39. }
  40. public final class MessageQueue {
  41. @UnsupportedAppUsage
  42. Message mMessages;
  43. ...
  44. boolean enqueueMessage(Message msg, long when) {
  45. ...
  46. synchronized ( this) {
  47. ...
  48. }
  49. ...
  50. }
  51. }

②取消息,如何保证线程安全

取消息的时候,锁的是MessageQueue,以此来保证取消息线程安全


  
  1. public final class MessageQueue {
  2. @UnsupportedAppUsage
  3. Message next() {
  4. ...
  5. for (;;) {
  6. ...
  7. synchronized ( this) {
  8. ...
  9. }
  10. }
  11. }
  12. }

6. 我们使用Message的时候,应该如何创建它?

使用 Message message = Message.obtain(); 创建,这里运用了享元设计模式


  
  1. public final class Message implements Parcelable {
  2. private static Message sPool;
  3. ...
  4. public static Message obtain() {
  5. synchronized (sPoolSync) {
  6. if (sPool != null) {
  7. Message m = sPool;
  8. sPool = m.next;
  9. m.next = null;
  10. m.flags = 0; // clear in-use flag
  11. sPoolSize--;
  12. return m;
  13. }
  14. }
  15. return new Message();
  16. }
  17. ...
  18. @UnsupportedAppUsage
  19. void recycleUnchecked() {
  20. flags = FLAG_IN_USE;
  21. what = 0;
  22. arg1 = 0;
  23. arg2 = 0;
  24. obj = null;
  25. replyTo = null;
  26. sendingUid = UID_NONE;
  27. workSourceUid = UID_NONE;
  28. when = 0;
  29. target = null;
  30. callback = null;
  31. data = null;
  32. synchronized (sPoolSync) {
  33. if (sPoolSize < MAX_POOL_SIZE) {
  34. next = sPool;
  35. sPool = this;
  36. sPoolSize++;
  37. }
  38. }
  39. }
  40. }
  41. public final class Looper {
  42. public static void loop() {
  43. ...
  44. for (;;) {
  45. ...
  46. msg.recycleUnchecked();
  47. ...
  48. }
  49. }
  50. }

7.handler的消息阻塞是怎么实现的,为什么主线程不会阻塞

Looper对象调用loop方法的next()方法会阻塞

当一个消息进入队列中的时候,会唤醒阻塞的loop()方法


  
  1. public final class MessageQueue {
  2. boolean enqueueMessage(Message msg, long when) {
  3. if (msg.target == null) {
  4. throw new IllegalArgumentException( "Message must have a target.");
  5. }
  6. synchronized ( this) {
  7. if (msg.isInUse()) {
  8. throw new IllegalStateException(msg + " This message is already in use.");
  9. }
  10. if (mQuitting) {
  11. IllegalStateException e = new IllegalStateException(
  12. msg.target + " sending message to a Handler on a dead thread");
  13. Log.w(TAG, e.getMessage(), e);
  14. msg.recycle();
  15. return false;
  16. }
  17. msg.markInUse();
  18. msg.when = when;
  19. Message p = mMessages;
  20. boolean needWake;
  21. //将msg插入到消息队列的头部,并将mMessages指向当前msg
  22. if (p == null || when == 0 || when < p.when) {
  23. // New head, wake up the event queue if blocked.
  24. msg.next = p;
  25. mMessages = msg;
  26. needWake = mBlocked;
  27. } else {
  28. needWake = mBlocked && p.target == null && msg.isAsynchronous();
  29. Message prev;
  30. //队列以时间顺序,从头到尾依次较小,将当前msg插入到合适的位置mMessages
  31. for (;;) {
  32. prev = p;
  33. p = p.next;
  34. if (p == null || when < p.when) {
  35. break;
  36. }
  37. if (needWake && p.isAsynchronous()) {
  38. needWake = false;
  39. }
  40. }
  41. msg.next = p; // invariant: p == prev.next
  42. prev.next = msg;
  43. }
  44. if (needWake) {
  45. nativeWake(mPtr);
  46. }
  47. }
  48. return true;
  49. }
  50. }

 


转载:https://blog.csdn.net/qq_43204550/article/details/116541351
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场