美文网首页面试题
最完整的Handler机制解析,一看就懂!!!

最完整的Handler机制解析,一看就懂!!!

作者: 鹿小纯0831 | 来源:发表于2019-03-12 23:59 被阅读2次

    故事要先从一个我们平常最常见的也是最简单的Handler的用法说起:

    Handler myHandler = new Handler() {
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case 1:
                        tv.setText("Hello World");
                        break;
                }
                super.handleMessage(msg);
            }
        };
    
    new Thread(new Runnable() {
                @Override
                public void run() {
                    //do something
                    Message message = Message.obtain();
                    message.what = 1;
                    myHandler.sendMessage(message);
                }
            }).start();
    

    这就是我们开始学习安卓时候最常见的一段代码,子线程执行完一系列操作之后,返回主线程刷新UI界面。

    一、看图说话

    一切的一切都要从一张图说起:
    [图片上传失败...(image-c47960-1552406344345)]
    说到这里就要请出本次的几位主演了:
    Handler:它有两个作用:

    • 将消息放入消息队列
    • 将消息从队列中取出并执行

    MessageQueue:消息队列
    Message:消息
    Looper:为线程运行消息循环。

    二、从主线程的Looper说起

    知道了四个类的大概作用,我们切回到开篇的代码。
    已知在activity中:

    public static void main(String[] args) {
    
            //与本文无关的代码
            ...
    
            Looper.prepareMainLooper();
    
            ActivityThread thread = new ActivityThread();
            thread.attach(false);////建立Binder通道 (创建新线程)
    
            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }
    
            if (false) {
                Looper.myLooper().setMessageLogging(new
                        LogPrinter(Log.DEBUG, "ActivityThread"));
            }
    
            // End of event ActivityThreadMain.
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            Looper.loop();
         //如果能执行下面方法,说明应用崩溃或者是退出了...
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }
    

    由代码可知,在activity中,已经执行了Looper.prepareMainLooper();方法和Looper.loop();方法。
    其中Looper.prepareMainLooper();就等同于Looper.prepare()方法。

    public static void prepare() {
            prepare(true);
        }
    
        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));
        }
    

    注意:这里出现了一个很重要的变量sThreadLocal,这个暂时先不讨论,之后会详细说明(问题1)。我们先看Looper的构造函数

    private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
        }
    

    Looper的构造函数里生成了一个消息队列(MessageQueue)并取得了当前的线程,即主线程。
    接下来我们来看looper.loop()方法,我只取了几行关键代码:

     public static void loop() {
            final Looper me = myLooper();
            final MessageQueue queue = me.mQueue;
    
            for (;;) {
                Message msg = queue.next(); // might block         
                try {
                    msg.target.dispatchMessage(msg);
                    end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
               
                msg.recycleUnchecked();
            }
        }
    

    先来看myLooper()这个方法:

    public static @Nullable Looper myLooper() {
            return sThreadLocal.get();
    }
    

    看,sThreadLocal这个变量又出现了,前面是set(Looper),这里是get返回一个Looper,也就是说sThreadLocal这个变量存储了我们主线程的Looper对象。
    另外这里有一个很重要的知识点:一个线程只有一个Looper。
    可以发现,loop方法的作用是:

    • 获取当前线程的Looper对象
    • 通过Looper对象获取到相应的消息队列
    • 之后对消息队列进行循环
    • 之后这一步就很关键啦,msg.target.dispatchMessage(msg);,这个我们移动到后面再讲(问题2)。

    好啦,主线程的Looper相关的方法讲完了,接下来该轮到我们的主角Handler了。

    三、先从Handler的构造函数谈起

    Handler myHandler = new Handler()
    

    我们同样舍弃了影响我们阅读的多余的代码;

    public Handler(Callback callback, boolean async) {
    
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
        }
    
    public static @Nullable Looper myLooper() {
            return sThreadLocal.get();
        }
    

    由代码可知,Handler的构造函数主要是获取了Looper对象和对应的消息队列。
    那好,现在问题出现了,我怎么知道我获取的这个Looper对象就是主线程的Looper对象呢?

    四、让我们回到问题1,重新认识一下sThreadLocal

    在Looper对象中:

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    

    我们先来认识一下ThreadLocal这个类:该类提供线程局部变量。 这些变量与其他对应类的不同之处就在于,它访问的每个线程(通过其 get或set方法)都有自己独立初始化的变量副本。
    上面这段话是Google源码中的说法,什么意思呢?我们直接用源码来说明:

    sThreadLocal.set(new Looper(quitAllowed));
    

    ThreadLocal代码中:

    public void set(T value) {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
        }
    
     ThreadLocalMap getMap(Thread t) {
            return t.threadLocals;
        }
    

    好了,到这里谜底终于揭开了,我们看到set方法显示取了当前的线程,也就是主线程,接下来通过getmap方法获取了主线程的ThreadLocal.ThreadLocalMap,然后把这个Looper对象存到了主线程中。这样主线程就跟我们的Looper对象关联起来了。
    同理,在Handler的构造函数中,我们也获取了一个Looper对象:

    public static @Nullable Looper myLooper() {
            return sThreadLocal.get();
        }
    
    public T get() {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null) {
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    T result = (T)e.value;
                    return result;
                }
            }
            return setInitialValue();
        }
    

    可以看出,get方法返回的是主线程的Looper对象。
    这样一来:HandlerLooper和主线程也就一一对应起来了。Handler所持有的Looper对象就是主线程的Looper对象。

    五、Handler发送消息到消息队列

    我们还是返回文章最开始的代码:

    new Thread(new Runnable() {
                @Override
                public void run() {
                    //do something
                    Message message = Message.obtain();
                    message.what = 1;
                    myHandler.sendMessage(message);
                }
            }).start();
    

    Message.obtain()这个方法没什么可说的,它的作用是从全局池返回一个新的Message实例,允许我们在许多情况下避免分配新对象。
    我们重点关注myHandler.sendMessage(message);这个方法:
    Handler类中,

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
            MessageQueue queue = mQueue;
            if (queue == null) {
                RuntimeException e = new RuntimeException(
                        this + " sendMessageAtTime() called with no mQueue");
                Log.w("Looper", e.getMessage(), e);
                return false;
            }
            return enqueueMessage(queue, msg, uptimeMillis);
        }
    
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    

    在这里我们看到,msg.target = this;this什么,就是我们Handler对象本身啊。我们把Handler对象赋值给了消息的target变量。这里有什么用呢?马上你就会知道了(问题3)。

    六、Handler获取对象并执行

    发送我们知道了,那么该怎么取呢?
    这里我们就要返回到第二部分的looper.loop()这个函数了,我们知道这个函数后面调用了msg.target.dispatchMessage(msg);函数,也就是我们留在上面的问题2。
    现在我们知道了,msg.target就是我们发送消息的Handler,接下来就是调用它的dispatchMessage(Message msg)方法了。

     public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    
     /**
         * Subclasses must implement this to receive messages.
         */
        public void handleMessage(Message msg) {
        }
    

    上面这个handleMessage方法就是我们在构造Handler时候重写的方法啦。

    相关文章

      网友评论

        本文标题:最完整的Handler机制解析,一看就懂!!!

        本文链接:https://www.haomeiwen.com/subject/wdpopqtx.html