• Handler


    Handler是Android中提供的一种异步回调机制,也可以理解为线程间的消息机制。为了避免ANR,我们通常会把一些耗时操作(比如:网络请求、I/O操作、复杂计算等)放到子线程中去执行,而当子线程需要修改UI时则子线程需要通知主线程去完成修改UI的操作,则此时就需要我们使用Handler机制来完成子线程与主线程之间的通信

    尽管Message的构造器是公开的,但是获取Message对象的最好方法是调用Message.obtain()或者Handler.obtainMessage(), 这样是从一个可回收对象池中获取Message对象。

    这两种方式都比直接new一个Message对象在性能上更优越.

    handler的构造方法

    Handler mHandler = new Handler();
    1. @Deprecated
    2. public Handler() {
    3. this(null, false);
    4. }

    获取当前Handler实例所在线程的Looper对象:mLooper = Looper.myLooper()
    如果Looper不为空,则获取Looper的消息队列,赋值给Handler的成员变量mQueue:mQueue = mLooper.mQueue
    可以设置Callback 来处理消息回调:mCallback = callback

    Handler是消息的处理者,但是它并不是最终处理消息的那个大佬,它有且只能有一个上级领导,就是Looper,Handler是将消息上报给Looper(领导),然后排队等待,等Looper(领导)处理完消息了,就会通知Handler去领取消息,给Handler分配任务,Handler拿到消息后在自行往下分发,Handler只能听命与Looper(领导)。

    1. public Handler(@Nullable Callback callback, boolean async) {
    2. if (FIND_POTENTIAL_LEAKS) {
    3. final Class<? extends Handler> klass = getClass();
    4. if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
    5. (klass.getModifiers() & Modifier.STATIC) == 0) {
    6. Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
    7. klass.getCanonicalName());
    8. }
    9. }
    10. mLooper = Looper.myLooper();//获取Looper
    11. if (mLooper == null) {
    12. throw new RuntimeException(
    13. "Can't create handler inside thread " + Thread.currentThread()
    14. + " that has not called Looper.prepare()");
    15. }
    16. mQueue = mLooper.mQueue;//获取MessageQuene
    17. mCallback = callback;
    18. mAsynchronous = async;
    19. }

     使用

    1. Handler mHandler = new Handler(){
    2. @Override
    3. public void handleMessage(@NonNull Message msg) {
    4. super.handleMessage(msg);
    5. }
    6. };

    1、拿到mHandler所在线程的Looper,当前mHandler是在Activity中创建的,很明显,当前的线程就是主线程,所以 mHandler的成员变量mLooper = Looper.myLooper(),此处就已经将当前的主线程Looper赋值过去了。
    2、紧接着,判断mLooper 是否为空,明显不为空,所以又会将主线程的消息队列赋值给mQueue。告诉Handler,你要是有消息,就送到这个消息队列中来,我(Looper)会一个个按顺序处理,处理完后我就会告诉你,你再处理。

    Handler只能绑定一个线程的Looper;

    Handler的消息是发送给Looper的消息队列MessageQueue,需要排队处理;

    handler的sendMessage方法 

    1. public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
    2. MessageQueue queue = mQueue;//获得当前的消息队列
    3. if (queue == null) { //若是在创建Handler时没有指定Looper,就不会有对应的消息队列queue ,自然就会为null
    4. RuntimeException e = new RuntimeException(
    5. this + " sendMessageAtTime() called with no mQueue");
    6. Log.w("Looper", e.getMessage(), e);
    7. return false;
    8. }
    9. return enqueueMessage(queue, msg, uptimeMillis);
    10. }
    1. private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
    2. long uptimeMillis) {
    3. msg.target = this;
    4. msg.workSourceUid = ThreadLocalWorkSource.getUid();
    5. if (mAsynchronous) {
    6. msg.setAsynchronous(true);
    7. }
    8. return queue.enqueueMessage(msg, uptimeMillis);
    9. }

    msg.target = this
    在发送消息到消息队列之前,明确的指定了消息的target为当前的Handler,以便于在后面Looper分发消息时用到。
    queue.enqueueMessage(msg, uptimeMillis)
    然后调用了消息队列的enqueueMessage()方法,并传递了两个参数,一个Message,一个是long型的时间。


    Handler dispatchMessage()方法

    消息发送后,交给Looper等待处理,处理完后会重新通知Handler处理,那么,是怎样通知Handler处理消息的呢?秘密就在dispatchMessage()这个方法中

    1. public void dispatchMessage(@NonNull Message msg) {
    2. if (msg.callback != null) {
    3. handleCallback(msg);
    4. } else {
    5. if (mCallback != null) {
    6. if (mCallback.handleMessage(msg)) {
    7. return;
    8. }
    9. }
    10. handleMessage(msg);
    11. }
    12. }

     

    当Looper处理完Message后,会使用到Message的target,即上面说到的target,即发送消息的那个Handler,Looper会调用Handler的dispatchMessage()方法分发消息,所以前面在enqueueMessage()发送消息的时候,为什么非得指明Message的target就是这个道理。

    回到dispatchMessage()这个方法:
    1、首先会判断Message的callback是否为空,此处的callback就是前面我们在Message中说到的,在静态方法创建Message时,可以指定的callback,若不为空,则将结果回调到callback中;
    2、若Handler的mCallback 不为空,也一样的道理。
    3、平时我们都没有传入这个callback,而是直接实现handleMessage()这个方法,在这个方法中处理更新UI任务。

    以上就是Handler发送和接收消息的基本过程:把消息发送到队列—>然后等待—>接收消息—>分发消息—>在回调中处理。

    MessageQueue

    Handler发送消息会调用MessageQueue的enqueueMessage()方法

    1. boolean enqueueMessage(Message msg, long when) {
    2. if (msg.target == null) { //判断msg的所属Handler
    3. throw new IllegalArgumentException("Message must have a target.");
    4. }
    5. synchronized (this) {//因为是队列,有先后之分,所以用了同步机制
    6. if (msg.isInUse()) {
    7. throw new IllegalStateException(msg + " This message is already in use.");
    8. }
    9. if (mQuitting) {
    10. IllegalStateException e = new IllegalStateException(
    11. msg.target + " sending message to a Handler on a dead thread");
    12. Log.w(TAG, e.getMessage(), e);
    13. msg.recycle();
    14. return false;
    15. }
    16. msg.markInUse();
    17. msg.when = when;
    18. Message p = mMessages;
    19. boolean needWake;
    20. //若队列为空,或者等待时间为0,或者比前面那位的等待时间要短,就插队
    21. if (p == null || when == 0 || when < p.when) {
    22. // New head, wake up the event queue if blocked.
    23. msg.next = p;
    24. mMessages = msg;
    25. needWake = mBlocked;
    26. } else {
    27. // Inserted within the middle of the queue. Usually we don't have to wake
    28. // up the event queue unless there is a barrier at the head of the queue
    29. // and the message is the earliest asynchronous message in the queue.
    30. needWake = mBlocked && p.target == null && msg.isAsynchronous();
    31. Message prev;
    32. //此处for循环是为了取出一个空的或者when比当前Message长的一个消息,然后进行插入
    33. for (;;) {
    34. prev = p;
    35. p = p.next;
    36. if (p == null || when < p.when) {
    37. break;
    38. }
    39. if (needWake && p.isAsynchronous()) {
    40. needWake = false;
    41. }
    42. }
    43. msg.next = p; // invariant: p == prev.next
    44. prev.next = msg;
    45. }
    46. // We can assume mPtr != 0 because mQuitting is false.
    47. if (needWake) {
    48. nativeWake(mPtr);
    49. }
    50. }
    51. return true;
    52. }

    以上就是消息队列插入消息的过程原理,通过单向链表的数据结构来存储消息。既然有了插入消息的方法供Handler插入消息,那么应该有对应的取出消息的方法,供Looper调用取出消息处理,它就是Message.next这个变量(结点)

    Looper

    Looper在Handler机制中扮演着关键的一环,他是循环处理消息的发动机,永不停息(永动鸡),它不断的从消息队列中取出的消息,处理,然后分发处理事件。每个线程都可以且只能绑定一个Looper。主线程之所以能处理消息,也是因为在APP启动时,在ActivityThread中的main()方法中就已经启动了Looper循环。
     

    1. public static void loop() {
    2. final Looper me = myLooper(); //获得当前的Looper
    3. if (me == null) {
    4. throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    5. }
    6. if (me.mInLoop) {
    7. Slog.w(TAG, "Loop again would have the queued messages be executed"
    8. + " before this one completed.");
    9. }
    10. me.mInLoop = true;
    11. // Make sure the identity of this thread is that of the local process,
    12. // and keep track of what that identity token actually is.
    13. Binder.clearCallingIdentity();
    14. final long ident = Binder.clearCallingIdentity();
    15. // Allow overriding a threshold with a system prop. e.g.
    16. // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
    17. final int thresholdOverride =
    18. SystemProperties.getInt("log.looper."
    19. + Process.myUid() + "."
    20. + Thread.currentThread().getName()
    21. + ".slow", 0);
    22. me.mSlowDeliveryDetected = false;
    23. for (;;) {
    24. if (!loopOnce(me, ident, thresholdOverride)) {
    25. return;
    26. }
    27. }
    28. }
    1. private static boolean loopOnce(final Looper me,
    2. final long ident, final int thresholdOverride) {
    3. Message msg = me.mQueue.next(); // might block //取出队头的消息
    4. if (msg == null) {
    5. // 如果消息为空,则跳过,继续执行下一个message
    6. // No message indicates that the message queue is quitting.
    7. return false;
    8. }
    9. // This must be in a local variable, in case a UI event sets the logger
    10. final Printer logging = me.mLogging;
    11. if (logging != null) {
    12. logging.println(">>>>> Dispatching to " + msg.target + " "
    13. + msg.callback + ": " + msg.what);
    14. }
    15. // Make sure the observer won't change while processing a transaction.
    16. final Observer observer = sObserver;
    17. final long traceTag = me.mTraceTag;
    18. long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
    19. long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
    20. if (thresholdOverride > 0) {
    21. slowDispatchThresholdMs = thresholdOverride;
    22. slowDeliveryThresholdMs = thresholdOverride;
    23. }
    24. final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
    25. final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
    26. final boolean needStartTime = logSlowDelivery || logSlowDispatch;
    27. final boolean needEndTime = logSlowDispatch;
    28. if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
    29. Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
    30. }
    31. final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
    32. final long dispatchEnd;
    33. Object token = null;
    34. if (observer != null) {
    35. token = observer.messageDispatchStarting();
    36. }
    37. long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
    38. try {
    39. msg.target.dispatchMessage(msg);
    40. if (observer != null) {
    41. observer.messageDispatched(token, msg);
    42. }
    43. dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
    44. } catch (Exception exception) {
    45. if (observer != null) {
    46. observer.dispatchingThrewException(token, msg, exception);
    47. }
    48. throw exception;
    49. } finally {
    50. ThreadLocalWorkSource.restore(origWorkSource);
    51. if (traceTag != 0) {
    52. Trace.traceEnd(traceTag);
    53. }
    54. }
    55. if (logSlowDelivery) {
    56. if (me.mSlowDeliveryDetected) {
    57. if ((dispatchStart - msg.when) <= 10) {
    58. Slog.w(TAG, "Drained");
    59. me.mSlowDeliveryDetected = false;
    60. }
    61. } else {
    62. if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
    63. msg)) {
    64. // Once we write a slow delivery log, suppress until the queue drains.
    65. me.mSlowDeliveryDetected = true;
    66. }
    67. }
    68. }
    69. if (logSlowDispatch) {
    70. showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
    71. }
    72. if (logging != null) {
    73. logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
    74. }
    75. // Make sure that during the course of dispatching the
    76. // identity of the thread wasn't corrupted.
    77. final long newIdent = Binder.clearCallingIdentity();
    78. if (ident != newIdent) {
    79. Log.wtf(TAG, "Thread identity changed from 0x"
    80. + Long.toHexString(ident) + " to 0x"
    81. + Long.toHexString(newIdent) + " while dispatching to "
    82. + msg.target.getClass().getName() + " "
    83. + msg.callback + " what=" + msg.what);
    84. }
    85. //回收可能正在使用的消息
    86. msg.recycleUnchecked();
    87. return true;
    88. }

    Looper的处理消息的循环还是挺简单的,就是拿出消息,然后分发,然后回收 

  • 相关阅读:
    Android基础篇 Android 数据存储与性能
    Clickhouse—MergeTree 数据生命周期
    wxpython中notebook控件的一些用法总结
    Uniapp 文件选择插件 Ba-FilePicker
    ansible User 模块
    【树莓派不吃灰】命令篇⑧ 校准树莓派时间
    【残差网络 论文泛读】……DenseNet……(Densely Connected Convolutional Networks)
    Tensorflow1架构内核和学习方法论
    JS------DAY2
    static关键字修饰成员变量与成员函数
  • 原文地址:https://blog.csdn.net/xiaowang_lj/article/details/127572567