美文网首页
源码各个击破之-Handler

源码各个击破之-Handler

作者: 修符道人 | 来源:发表于2019-09-29 10:31 被阅读0次

在面试的过程中,反复的被问到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解决线程问题的一种手段。


Looper的源码分析


  • 官方定义
    为一个线程启动消息循环,线程默认是没有消息循环的。

  • ActivityThread

    image
    没有错,主线程的Looper就是在此绑定的。这就是为什么在主线程里可以直接new Handler的原因,系统已经给我们执行了Looper.prepare()和Looper.loop()方法。

    如果你勤学好问,那么,请看ActivityThread这个类,它并不是继承自Thread,更不是我们常说的主线程(UI线程)。Android主线程到底是个什么,你真正明白吗?
    ActivityThread是如何启动的,可以参照我的这篇博客: (Android源码个个击破之Launcher)

上面的博客,作者说到了主线程是如何启动的


主线程的启动

所以要弄懂主线程的启动,你必须了解操作系统、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的构造方法里实例化的:


image.png
  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发送消息就会报这个错误。


    image.png

    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与线程无关

image.png

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-2766

Handler的处理确实是在子线程。
好吧,到这里自己打自己脸了。所以,科学要严谨,要多做验证测试。

相关文章

网友评论

      本文标题:源码各个击破之-Handler

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