[关闭]
@linux1s1s 2017-01-22T16:40:59.000000Z 字数 3012 阅读 3137

Handler 详解

AndroidMechanism 2016-10


这个详解基本小结了Handler的使用和相关的知识,在了解这些知识前,我们先来看看这些问题.

好了,如果上面的问题,你能对答如流,并且深知其中的原理那么没有必要继续看下去了,反之,就该好好补补了(^o^)/~。

Handler是什么

handler是Android给我们提供来更新UI的一套机制,也是一套消息处理的机制,我们可以发送消息,也可以通过塔来处理消息,handler在我们的framework中是非常常见的。

看完上面的解释,我们有个疑问,为什么要用Handler了?

因为android在设计的时候,就封装了一套消息创建、传递、处理机制,如果不遵循这样的机制就没有办法更新UI信息,就会抛出异常信息;

接下来我们就从源码的角度看一下Google是如何在线程中封装这么一套Handler机制的,我们先来找一下Java的入口函数Main方法。

此处输入图片的描述
在android.app.ActivityThread.java中。

接着我们重点关注 L4 L10 L20这三行代码,我们先一步一步跟进去

L4:
此处输入图片的描述
然后看看L88和L93
此处输入图片的描述

此处输入图片的描述

由上面的代码可以看到,一开始就初始化了一个Looper实例,然后将这个实例保存在sThreadLocal成员变量中,然后在需要的时候调用myLoop()方法,再把这个Looper实例取出来。所以很容易得到,一个线程对应唯一的Looper实例

L10:
在看第10行代码sMainThreadHandler = thread.getHandler();之前,先看看第6行代码
ActivityThread thread = new ActivityThread();
然后我们看一下ActivityThread这个类的成员变量:
此处输入图片的描述
第27行,可以看到一个很奇怪的类 H 类,成员变量 final H mH = new H();
接下来看看这个最精简的类:
此处输入图片的描述

是不是看到了我们熟悉的Activity的生命周期,对的,Activity的生命周期就是在这里触发的,所以最开始的L10sMainThreadHandler = thread.getHandler();取回的Handler实例就是这么一个H类的实例。

  1. final Handler getHandler() {
  2. return mH;
  3. }

L20:
此处输入图片的描述
上面的代码首先取得Looper实例,然后取得该实例的MessageQueue,然后死循环遍历这个queue。

至此,UI线程的Looper准备完毕。

子线程更新UI有几种典型方式,这些方式本质上是什么样的

既然Google为我们准备了Handler机制,所以我们在子线程中和主线程交互变得很简单了,那么子线程如何通过Handler和UI线程交互呢,又有哪些方法,这些交互的方式本质上是什么样子的呢,下面我们一起来探个究竟。

从Google给我们提供的SDK中我们大概知道有以下几种方式:

  1. Handler Post
  2. Handler SendMessage
  3. RunOnUIThread
  4. View Post

我们直接看一下上面四种交互方式的Demo:

此处输入图片的描述
此处输入图片的描述
此处输入图片的描述

这里特别补充一下L24添加父类构造器方法:

  1. public UpdateViewHandler(TextView textView)
  2. {
  3. super();
  4. this.textView = new WeakReference<>(textView);
  5. }

上面代码是java1.8引入Lambda表达式的写法。代码比较简单,这里不在赘述,我们接下来感兴趣的是,上面的四种交互方法的源码是神马样子的?

此处输入图片的描述

上面代码需要注意的是L37行msg.target = this;,这行代码说明,Handler发送消息总得有个接收方,而这个接收方不是别人,就是自己(Handler实例本身)。

此处输入图片的描述

上面的代码需要注意的是L3,这里的Message实例有两种方式,分别是
Message message = Message.obtain();Message message = new Message();这两种方式哪种更好还是没有区别?为什么,自己想一想。

此处输入图片的描述

如果不在UI线程,还是调用Handler的post

此处输入图片的描述

还是调用Handler的post,所以上面四种方式,本质上都是调用Handler的通信机制,所以在Android中子线程更新UI本质上都是通过Handler机制来处理。

子线程真的不能更新UI吗

通过上面的子线程更新UI四种方式,我们基本了解了Android的Handler机制,但是子线程真的不能更新UI吗?我们来做个试验好了。

此处输入图片的描述

运行以后,小伙伴都惊呆了,居然完美运行,这是神马情况?

我们先不直接回答这个问题,换个方式实现一下:

此处输入图片的描述

这样运行以后,是不是立马Crash了?并抛出Only the original thread that created a view hierarchy can touch its views.异常,这又是神马情况?

回答这个问题还是需要回到ActvityThread这个类中去找答案,具体的细节这里不在赘述了,直接给出原因吧,onCreate生命周期的时候ViewParent还没有初始化完毕,这个时候在View上更新UI的时候会引起重绘,直接来看看代码:

此处输入图片的描述

注意L16行,p为空,所以不会执行p.invalidateChild(this, damage);方法
我们看看这个方法是弄啥咧。

此处输入图片的描述
是不是看到熟悉的异常出现在哪里了,以上的诡异事件是不是到此就弄清楚了,主要是ViewParent没有初始化完毕,所以不会执行UI线程检查,如果一旦初始化完毕,必然检查UI线程的更新操作,这个时候任何子线程企图更新UI的操作都无处遁形了。

HandlerThread是什么

我们首先不用HandlerThread小试一下。

此处输入图片的描述
此处输入图片的描述
此处输入图片的描述

代码的思路是么有问题的,主线程和子线程先沟通,然后子线程有结果以后再和主线程沟通,这个逻辑没有问题,但是我们run的时候提示在L66 有个空指针异常,这个问题是由于多线程并非引起的,因为这个looper还没有来得及初始化,所以报了个空指针异常,那么Google给我们提供的HandlerThread就做了同步处理,所以如果将我们自己写的带有Looper的子线程换成HandlerThread就不会有这个多线程并发引起的问题。

主线程和子线程之间如何相互通信

上面已经基本回答了主线程和子线程之间如何相互通信,一般建议使用HandlerThread,如果不乐意直接使用,也可以自己定义一个带有Looper的Thread的,但是必须处理好多线程并发问题。另外,对于Handler的构造器,其中有个参数是Callback,这个Callback作用是什么?看看源码一切皆清楚了

此处输入图片的描述

很明显的L35行,如果这个callback返回的是true 就不会继续往下执行了,所以这个callback有截获的作用。

关于Handler的介绍到这里基本结束了,当然Handler的其他知识还有不少,这里不在赘述。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注