美文网首页面试题
最完整的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