手撕Handler

前言

在平常开发中,咱们势必会使用到子线程和UI线程的通讯,而起着桥梁做用的就是咱们经常使用的Handler。可是他的内部是怎么运做的?运做的过程当中存在什么问题?须要咱们注意,本文将会详细讲解。java

解析Handler

从图中咱们就能够知道了,整个Handler工做组成的包括了HandlerLooperMessageQueueMessage这四个部分。git

MessageQueue和Message分别只是一个队列和消息实体类,天然再也不多说。 而Handler和Looper的具体是怎样的呢?github

在个人模拟Handler项目中,已经比较清晰的阐述了整个框架的工做流程,接下里就是结合SDK代码的一份解析了。编程

整个Handler往简单了来讲其实就干了两件事情:安全

  • 发送消息
  • 处理消息

发送消息

涉及到的三个函数sendMessage()enqueueMessage()Looper.prepareMainLooper()框架

全部事情的起源要从Looper.prepareMainLooper()开始讲起。 这个函数处于ActivityThread中,没有了解过这个类的读者们须要知道,java编程必定是有一个主入口的,可是咱们在整个Android编程中,历来没有涉及过main()这个函数,是由于它已经包含在了ActivityThread这个类中,而它已经通过了复杂的封装。ide

接下来看下这个Looper.prepareMainLooper ()函数。函数

public static void prepareMainLooper() {
        prepare(false); // 1
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper(); // 2
        }
    }
// 上述注释1对应的函数
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));
    }
// 上述注释2对应的函数
public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
复制代码

两小段代码,里面用到了一个变量sThreadLocal,这个变量是使用static final修饰的,意味这全局惟一。从他主动抛出的异常咱们也能够看出Looper这个对象也是一个惟一的变量。这是咱们须要掌握的第一个知识点。oop

接下来是关于sendMessage()函数 这个函数实际上是一个泛称,他并不仅仅指sendMessage(),他还能够是sendMessageAtTime()sendMessageDelayed(),他们都干了一件事情——传递消息。经过一直向下探索,你就能知道他们最后调用的都是enqueueMessage()这个函数,也就是把消息放进了消息队列中。post

没有不少的操做,就是咱们熟悉的链表操做。这里没有作展现,有兴趣的朋友进到源码往下翻一点,立刻就能看到了。

就这样很简单,而且很成功的让咱们的消息进入了消息队列。

处理消息

接收完消息,咱们要干吗?咱们为何要发消息,由于咱们要处理啊。

这里咱们要遇到的函数有:Looper.loop()dispatchMessage()handleMessage()。 用过Handler的读者们都应该知道咱们是须要重写handleMessage()这个函数的,用于对不一样的消息做出响应,因此就再也不多介绍。 因此第一个讲的就是Looper.loop()这个函数。

Looper源码截图1

Looper源码截图2

一共两段代码,也是相当重要的一部分。 在这个代码中两个相当重要的点: (1)首先是问题是这么一个死循环的函数,怎么就没引起ANR呢???? (2)经过dispatchMessage()如何分发这个消息的?target是什么?

先是第一个问题的解答。 网上的解答多种多样。可是最关键的点实际上是这样的,ANR是围绕loop()这个函数展开的,而ANR的出现也就是loop()的消息没有获得及时的消费。

第二个问题。 先说target这个爆红的变量是什么。msg.target也就是说这是Message的一个元素,搜索Message就能找到以下图示。

原来target就是一个Handler,而这个Handler就是咱们对应的主动建立Handler。 而后就是dispatchMessage()函数了。

/** * Handle system messages here. */
    public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
复制代码

想来这就很清楚了,他也就是将事件分发给了handleMessage()处理,而handleMessage()又是咱们本身来专门写的。msg.callback是一个Runnable对象。

害,原来就是这样啊。。

思考

  1. Handler的内存泄漏实例。
  2. 为何Handler不能在子线程建立?
  3. 为何Handler构造方法里面的Looper不是new出来的?

问题1:Handler的内存泄漏实例。

Handler handler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

         handler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(@NonNull Message msg) {
                startActivity(new Intent(MainActivity.this, HandlerTestActivity.class));
                return false;
            }
        });

        new Thread(new Runnable() {
            @Override
            public void run() {
                // 中断3秒
                SystemClock.sleep(3000);
                handler.sendEmptyMessage(0);
            }
        }).start();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.e("onDestroy", "已销毁");
        handler.removeCallbacksAndMessages(0); // 2
        handler = null; // 1
    }
复制代码

若是不加注释1和注释2,这就是一段会内存泄露的代码,看了代码,应该也清楚逻辑十分简单,就是一个跳转。推出程序后,你仍然会看到跳转,这就是Handler的内存泄漏。

问题2: 为何Handler不能在子线程建立?

这个问题其实有点问题,对于修改过底层的华为的操做系统并不存在这样的问题,可是正常的Android原生系统就不行了。 代码以下

new Thread(new Runnable() {
            @Override
            public void run() {

                handler = new Handler(new Handler.Callback() {
                    @Override
                    public boolean handleMessage(@NonNull Message msg) {
                        return false;
                    }
                });
            }
        }).start();
复制代码

问题2报错

经过对报错溯源,咱们就能发现这样一个问题。

他拿不到 Looper,由于他不是UI线程。 其实这就是问题所在,咱们上文讲过 sThreadLocal这个变量,他经过一个 get()函数获取了 Looper。可是这里存在一个问题,这个 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();
    }
复制代码

原来,他的取法就是从一个Map中进行获取的,而key,就是当前线程,因此当咱们在子线程中建立Handler的时候,咱们也是照样拿不到Looper的,这个时候咱们一样明白了Looper也是一个惟一的,由于他不会为咱们建立出来的一个子线程再添加一个Looper,而是共用。

就这个问题,Google其实有给出解决方案,详细请看 》》HandlerThread那些事儿

问题3:为何Handler构造方法里面的Looper不是new出来的?

这个问题的性质和问题2有点相似了,惟一性。Looper做为一个事件处理的重要组成部分,想来咱们已经看到了,就像多道程序设计技术同样,这是一个不受控制的过程,咱们须要疯狂的思考安全性,同步性等问题。这也是惟一性的好处,因此事件统一处理,处理起来也就有序。至少在咱们的平时使用中已经证实了这是一个可取的方法。

以上就是个人学习成果,若是有什么我没有思考到的地方或是文章内存在错误,欢迎与我分享。


相关文章推荐:

手撕OkHttp

手撕ButterKnife

HandlerThread那些事儿

手撕AsyncTask

相关文章
相关标签/搜索