在面试的过程中,反复的被问到Handler机制,在此对其做一个深入的剖析.下面Handler的源码都是参照API28的。
目录
Google源码对Handler的定义
    看Handler源码开关注释:
    Handler允许你发送和处理和线程的MessageQueue相关的Message和Runnable对象,每一个Handler对象都对应一个单一的线程。
一.先看看我们平时怎么使用Handler
   平时我们Handler用的最多的就是子线程刷新UI,在Activity里new一个Handler,实现handleMessage方法,然后在子线程里调用handler实例发送消息,handleMessage处理消息,刷新UI.二.那怎么Handler就能实现刷新UI的呢
         就是,怎么就从子线程切到主线程了呢?建议大家有空看看操作系统的入门书籍,理解下线程和进程的概念.(我给大家推荐一本吧<<操作系统真象还原>>,第9章,详细介绍了线程和进程的概念和区别)1.Handler创建
1)通过构造方法创建
第1种,不带Looper参数。
     public Handler(Callback callback, boolean async)     
 第2种,带Looper参数。
    public Handler(Looper looper, Callback callback, boolean async)注意这个Callback的实现方法handleMessage的返回值为true,则Handler的handleMessage不会被执行。看下面的这个源码就能明白:
    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {        //这里
                    return;
                }
            }
            handleMessage(msg);
        }
    }- 
子线程中创建Handler在子线程中,不可直接调用Handler的构造函数创建Handler对象,否则会报错: Can't create handler inside thread xxx that has not called Looper.prepare()看源码可以知道Handler的构造方法获取的Looper为空就会报这个错 mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()"); }那么疑问来了,我们平时使用Handler的时候,直接就是在Activity里new Handler,也没有什么设置Looper的操作,为啥就没有报错呢?请往下看。 
- 
在Activity里直接创建Handler看Looper.myLooper()方法,官方的解释就是获取和当前线程关联的Looper。 /** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static @Nullable Looper myLooper() { return sThreadLocal.get(); }只有Looper.prepare()方法被调用了,sThreadLocal才会有值。 // sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();那么就找到一个线索了,主线程肯定某个地方调用了Looper.prepare(),so where is it? 
强烈建议详细(滴水不漏)看一遍ThreadLocal和Looper的源码,答案自然就揭晓了。
ThreadLocal的源码分析
ThreadLocal类并非是android特有的类,而是Java解决线程问题的一种手段。
- AtomicInteger (原子自增操作,线程安全)
 https://blog.csdn.net/a260724032/article/details/81940785
 https://blog.csdn.net/fanrenxiang/article/details/80623884
- 
ThreadLocal定义 
 https://www.jianshu.com/p/6fc3bba12f38 (推荐)
 https://www.jianshu.com/p/69ae8c213b30
 https://www.imooc.com/article/45196
 https://blog.csdn.net/u011860731/article/details/48733073看了这么多文章,也没有对ThreadLocal这个类完完全全地理解。大致的意思就是ThreadLocal通过变量备份的方法解决了线程安全问题。 
ThreadLocal类源码分析:
1)get方法:获取当前线程t,然后获取当前线程t的ThreadLocalMap变量,通过传入当前ThreadLocal变量作为key,获取value。
**在Looper类中,ThreadLocal就起着为线程存放记录Looper的作用。**Looper的源码分析
- 
官方定义 
 为一个线程启动消息循环,线程默认是没有消息循环的。
- 
ActivityThread 
  
 没有错,主线程的Looper就是在此绑定的。这就是为什么在主线程里可以直接new Handler的原因,系统已经给我们执行了Looper.prepare()和Looper.loop()方法。如果你勤学好问,那么,请看ActivityThread这个类,它并不是继承自Thread,更不是我们常说的主线程(UI线程)。Android主线程到底是个什么,你真正明白吗? 
 ActivityThread是如何启动的,可以参照我的这篇博客: (Android源码个个击破之Launcher)
- 
什么是主线程 
 https://blog.csdn.net/u011631275/article/details/47337385
 在此我们需要明白主线程和Thread类没有什么关联,因为一旦使用了Thread,它就是一个子线程了。上面的博客,作者说到了主线程是如何启动的 
  
 所以要弄懂主线程的启动,你必须了解操作系统、Linux操作系统、进程、线程的概念,了解从手机开机到Lancher启动的全部流程。
- 
Looper的其它作用场景 
 有的代码,它是要一直循环不断地执行的。比如操作系统,一启动,就会开启“死”循环,监视管理各个进程。
 在Android里,很多处都会用到Looper消息循环机制, 如SystemServer的run方法,还有Activity的各个生命周期的方法都是通过Looper机制回调的。
可以这么说,Looper机制保证了线程永远不会执行完,是Looper给了线程生命。
- 
loop方法 
 Looper的loop方法就是负责不断地从消息队列里取出消息,然后交给Handler分发处理。for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } try { msg.target.dispatchMessage(msg); dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } }
- 
 Looper.loop死循环为什么不会卡死? 
 https://blog.csdn.net/cshao888/article/details/78386593
 涉及到Linux底层 pipe/epoll机制知识
 https://blog.csdn.net/yangwen123/article/details/14118733
 所以说android有庞大的知识框架,怎么学也学不完,学了java还要学Linux。
2)通过静态方法创建
注意8.0之前的Handler是没有静态方法来创建的。
静态方法最终调用的还是构造方法
    //9.0开始有此方法
 @NonNull
    public static Handler createAsync(@NonNull Looper looper) {
    }
  //9.0开始有此方法
    @NonNull
    public static Handler createAsync(@NonNull Looper looper, @NonNull Callback callback) {
    }
        //8.0开始有此方法
    /** @hide */
    @NonNull
    public static Handler getMain() {
    }
   //8.0开始有此方法
    /** @hide */
    @NonNull
    public static Handler mainIfNull(@Nullable Handler handler) {
    }2.Handler使用
上面创建了Handler,现在我们就来使用Handler
1)执行Runnable
    /**
     * Causes the Runnable r to be added to the message queue.
     * The runnable will be run on the thread to which this handler is 
     * attached. 
     *  
     * @param r The Runnable that will be executed.
     * 
     * @return Returns true if the Runnable was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }看看官方的注释,执行runnable就是在Handler关联的线程里执行runnable。
可以看到postXXX各种方法,最终还是通过getPostMessage方法将Runnable封装在Message里
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }然后执行sendMessageXXX方法,最终也就是执行sendMessageAtTime方法。
post与sendMessage的区别就在于post,Handler的handleMessage是没有回调的,至于为什么没有回调下面会讲到。
    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {        //这里
                    return;
                }
            }
            handleMessage(msg);
        }
    }    private static void handleCallback(Message message) {
        message.callback.run();
    }可见Handler的post方法,只是执行了一下Runnable的run方法。
2)发送Message
下面看sendMessageAtTime方法的源码
 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);
    }这个queue是在Handler创建的时候,通过Looper获取的,在Looper的构造方法里实例化的:

  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就是表示消息要给送给这个Handler。
下面看看queue.enqueueMessage这个方法
MessageQueue(难点)
https://blog.csdn.net/u013749540/article/details/79403851 (贴了不少源码,很难看懂)
- 官方定义
 持有由Looper分发的消息列表的低级类,消息并不是直接添加到MessageQueue,而是通过和Looper相关联的Handler。
- enqueueMessage方法 (将消息加入消息队列)
 1)如果和Handler绑定的线程死了,还去调用Handler发送消息就会报这个错误。
  
 2)
- next方法
 用到了很多的Native方法。
3.言归正传
上面研究了这么多,已经讲明白了Handler是如何发送消息又是如何接受的消息-消息循环机制。
- Handler怎么就把线程跨了呢?
 https://www.jianshu.com/p/465329c76770 (言简意赅的说明问题)
 线程、Looper、Handler三者绑定,looper的loop()方法一直在绑定的这个线程里运行,并且解析执行Message,无论你Handler是在哪个线程发送消息,handleMessage始终在绑定的线程里执行。
记住这个定律:
1)决定跨线程的是Looper类,只有Looper与线程相关。
2)Handler类只是最顶层的一个接口,负责与Looper的消息队列交互。Handler与线程无关
4.错误的理解Handler
一般Handler在Activity(主线程)里直接new,在子线程里要开启Looper.prepare,然后再new Handler。
所以不知不觉我就形成了一种错觉:Handler在哪个线程里new的,它的消息就会分发到哪个线程。
带着这个“错觉”,然后去了小米面试,面试官就提出了问题:如果在主线程里创建Handler,然后使用子线程的Loopper呢?
确实,Handler有个带Looper的构造方法:
/**
     * Use the provided {@link Looper} instead of the default one.
     * @param looper The looper, must not be null.
     */
    public Handler(@NonNull Looper looper) {
        this(looper, null, false);
    }看注释里“the default one”,确定,如果我new无参的Handler,“the default one”就是线程已经绑定好的Looper。所以我的错觉“Handler在哪个线程里new的,它的消息就会分发到哪个线程。”如果加上一个限定,就是new调用的是Handler的无参构造方法就没啥毛病了。
验证带参Handler
首先定义一个子线程
public class TestThread extends Thread {
    private static final String TAG = "TestThread";
    private  OnLooperPreparedListener onLooperPreparedListener;
    public TestThread(OnLooperPreparedListener onLooperPreparedListener) {
        this.onLooperPreparedListener = onLooperPreparedListener;
    }
    @Override
    public void run() {
        super.run();
        Log.e(TAG, "TestThread name : " + getName());
        Looper.prepare();
        if(onLooperPreparedListener != null){
            onLooperPreparedListener.onLooperPrepare(Looper.myLooper());
        }
        Looper.loop();
    }
    public interface  OnLooperPreparedListener{
        void onLooperPrepare(Looper looper);
    }
}在activity里调用
public class HandlerTestActivity extends AppCompatActivity {
    private static final String TAG = "HandlerTestActivity";
    private class MyCallBack implements   Handler.Callback{
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            Log.e(TAG, "handleMessage: " + msg.what + "," + Thread.currentThread().getName());
            return false;
        }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_test);
        TestThread thread = new TestThread(new TestThread.OnLooperPreparedListener() {
            @Override
            public void onLooperPrepare(Looper looper) {
                Handler handler = new Handler(looper,new MyCallBack());
                handler.sendEmptyMessage(0);
            }
        });
        thread.start();
    }
}输出日志
TestThread: TestThread name : Thread-2766
HandlerTestActivity: handleMessage: 0,Thread-2766Handler的处理确实是在子线程。
好吧,到这里自己打自己脸了。所以,科学要严谨,要多做验证测试。

0 条评论