• 【Handler机制分析】


    Handler基本解析

    Handler是Android系统提供的一种异步回调机制,也可以理解成线程的消息机制。为了避免ANR,我们通常会把一些耗时的操作(I/O,网络请求,复杂计算等)放到子线程去执行,执行完毕之后需要通知主线程更新UI操作,这个时候就需要Handler机制来处理

    基本使用

    在主线程中创建Handler实例,并实现handleMessage方法

     private Handler handler = new Handler(){
     @Override
     public void handleMessage(Message msg) {
         switch (msg.what) {
                //执行相关修改UI的操作
            }
        }
     };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在子线程中获取Handler对象,并在需要更新ui的地方用获取的handler对象发送消息;

     Message msg = Message.obtain();
     msg.obj = "content";
     msg.what = 1;
     //发送消息给Handler
     handler.sendMessage(msg); 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    源码解析

    每个Handler都会关联一个消息队列,消息队列又是封装在Looper当中的,每个Looper又会关联一个线程。这样三者就关联上了。

    Handler是一个消息处理器,将消息发送给消息队列,然后再由相应的线程从消息队列中逐个取出,并执行。

    主线程的消息队列是在Looper.prepareMainLooper()方法创建的,最后执行Looper.loop() 方法来循环取消息

    三者关系

    下边我们结合代码一起来一下:

    首先来看Handler的构造函数

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

    从Handler的初始化,我们可以看到,初始化的同时会通过Looper.myLooper()获取了一个Looper对象,并与Looper进行了关联,然后通过Looper对象获取了消息队列。我们继续深入到Looper的源码中Looper.myLooper()是怎么实现的。

      public static void prepareMainLooper() {
            prepare(false);
            synchronized (Looper.class) {
                if (sMainLooper != null) {
                    throw new IllegalStateException("The main Looper has already been prepared.");
                }
                sMainLooper = myLooper();
            }
        }
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
       private static void prepare(boolean quitAllowed) {
            if (sThreadLocal.get() != null) {
                throw new RuntimeException("Only one Looper may be created per thread");
            }
            sThreadLocal.set(new Looper(quitAllowed));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    从上面的程序可以看出,通过prepareMainLooper(),内部调用prepare(boolean quitAllowed)方法创建了一个Looper对象,并通过sThreadLocal.set(new Looper(quitAllowed))方法,设置给了sThreadLocal;

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

    通过Looper的预备工作,sThreadLocal中存储了一个Looper对象,然后myLooper()方法通过sTreadLocal获取到了Looper对象,那么消息队列就跟线程关联上了,所以各个线程只能访问自己的looper。

    综上所述,我们可以发现,消息队列通过Looper与线程关联,而Looper又是跟Handler关联的,所以这三者就有了联系。

    如何执行消息循环

    在创建Looper对象后,通过Handler发来的消息放在消息队列中后是如何被处理的呢?这就涉及到了消息循环,消息循环是通过Looper.loop()方法来建立的。看代码:

    public static void loop() {
            final Looper me = myLooper();
            if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            }
            if (me.mInLoop) {
                Slog.w(TAG, "Loop again would have the queued messages be executed"
                        + " before this one completed.");
            }
    
            me.mInLoop = true;
            final MessageQueue queue = me.mQueue;
    //省略
    
            // Allow overriding a threshold with a system prop. e.g.
            // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
            final int thresholdOverride =
                    SystemProperties.getInt("log.looper."
                            + Process.myUid() + "."
                            + Thread.currentThread().getName()
                            + ".slow", 0);
    
            boolean slowDeliveryDetected = false;
    
            for (;;) {
                Message msg = queue.next(); // might block
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
    
                // This must be in a local variable, in case a UI event sets the logger
                final Printer logging = me.mLogging;
                if (logging != null) {
                    logging.println(">>>>> Dispatching to " + msg.target + " " +
                            msg.callback + ": " + msg.what);
                }
                // Make sure the observer won't change while processing a transaction.
                final Observer observer = sObserver;
    
                final long traceTag = me.mTraceTag;
                long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
                long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
                if (thresholdOverride > 0) {
                    slowDispatchThresholdMs = thresholdOverride;
                    slowDeliveryThresholdMs = thresholdOverride;
                }
                final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
                final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
    
                final boolean needStartTime = logSlowDelivery || logSlowDispatch;
                final boolean needEndTime = logSlowDispatch;
    
                if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                    Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
                }
    
                final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
                final long dispatchEnd;
                Object token = null;
                if (observer != null) {
                    token = observer.messageDispatchStarting();
                }
                long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
                try {
                    msg.target.dispatchMessage(msg);
                    if (observer != null) {
                        observer.messageDispatched(token, msg);
                    }
                    dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
                } catch (Exception exception) {
                    if (observer != null) {
                        observer.dispatchingThrewException(token, msg, exception);
                    }
                    throw exception;
                } finally {
                    ThreadLocalWorkSource.restore(origWorkSource);
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
                if (logSlowDelivery) {
                    if (slowDeliveryDetected) {
                        if ((dispatchStart - msg.when) <= 10) {
                            Slog.w(TAG, "Drained");
                            slowDeliveryDetected = false;
                        }
                    } else {
                        if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                                msg)) {
                            // Once we write a slow delivery log, suppress until the queue drains.
                            slowDeliveryDetected = true;
                        }
                    }
                }
                if (logSlowDispatch) {
                    showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
                }
    
                if (logging != null) {
                    logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
                }
    
                // Make sure that during the course of dispatching the
                // identity of the thread wasn't corrupted.
                final long newIdent = Binder.clearCallingIdentity();
                if (ident != newIdent) {
                    Log.wtf(TAG, "Thread identity changed from 0x"
                            + Long.toHexString(ident) + " to 0x"
                            + Long.toHexString(newIdent) + " while dispatching to "
                            + msg.target.getClass().getName() + " "
                            + msg.callback + " what=" + msg.what);
                }
    
                msg.recycleUnchecked();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117

    只看主干线路,loop()方法实质上就是通过一个死循环不断的从消息队列中获取消息,然后又不断的处理消息的过程。

    消息的处理

    我们从loop中的 dispatchMessage()方法入手,看看谁是该方法的调用者,深入Message源码中看看target的具体类型:

    public final class Message implements Parcelable {
     public long when;
    
        /*package*/ Bundle data;
    
        @UnsupportedAppUsage
        /*package*/ Handler target;
    
        @UnsupportedAppUsage
        /*package*/ Runnable callback;
    
        // sometimes we store linked lists of these things
        @UnsupportedAppUsage
        /*package*/ Message next;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    源码中我们可以看到其实target就是Handler类型。所以Handler是将消息发送到消息队列暂时存储下,然后又将消息发送给Handler自身去处理。那我们继续到Handler源码中去看看Handler是如何处理消息的:

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    } 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    private static void handleCallback(Message message) {
        message.callback.run();
    } 
    
    • 1
    • 2
    • 3
    /**
     * Subclasses must implement this to receive messages.
     * 消息处理方法为一个空方法,由子类去实现
     */
    public void handleMessage(Message msg) {
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    从上面的源码中可以看出,dispatchMessage(Message msg)只负责分发Message。从Message源码中我么可以知道callback为Runnable类型,如果callback不为空,则执行 handleCallback方法来处理,而该方法又会调用callback.run();如果如果callback为空,则调用handleMessage来处理消息,而该方法又为空,所以我们会在子类中重写该方法,并将修改UI的代码写在里面。之所以会出现这两种情况,是因为Handler发送消息有两种形式:

    在本文的一般使用步骤中,我使用的是sendMessage(msg)发送消息,此时callback就为空。
    当使用post发送消息时,callback就不为空。
    以上就是Handler机制的原理,大致可以总结为:在子线程中Handler将消息发送到MessageQueue中,然后Looper不断的从MessageQueue中读取消息,并调用Handler的dispatchMessage发送消息,最后再Handler来处理消息。

  • 相关阅读:
    椭圆曲线加密算法中公钥与私钥互换性分析
    【Linux】传输层协议:TCP/UDP
    IP协议(下)
    Rust-类型转换进阶
    关于VS多个dll相互依赖的问题
    C++中的单例设计模式在多线程中的使用
    heatmap | cell cycle genes in Seurat
    8. SAP ABAP OData 服务如何支持创建(Create)操作
    关于数据权限的设计
    OJ练习第160题——LRU 缓存
  • 原文地址:https://blog.csdn.net/jack22001/article/details/127648513