@linux1s1s
2017-01-22T16:40:59.000000Z
字数 3012
阅读 3162
AndroidMechanism
2016-10
这个详解基本小结了Handler的使用和相关的知识,在了解这些知识前,我们先来看看这些问题.
好了,如果上面的问题,你能对答如流,并且深知其中的原理那么没有必要继续看下去了,反之,就该好好补补了(^o^)/~。
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类的实例。
final Handler getHandler() {
return mH;
}
L20:
上面的代码首先取得Looper实例,然后取得该实例的MessageQueue
,然后死循环遍历这个queue。
至此,UI线程的Looper准备完毕。
既然Google为我们准备了Handler机制,所以我们在子线程中和主线程交互变得很简单了,那么子线程如何通过Handler和UI线程交互呢,又有哪些方法,这些交互的方式本质上是什么样子的呢,下面我们一起来探个究竟。
从Google给我们提供的SDK中我们大概知道有以下几种方式:
- Handler Post
- Handler SendMessage
- RunOnUIThread
- View Post
我们直接看一下上面四种交互方式的Demo:
这里特别补充一下L24添加父类构造器方法:
public UpdateViewHandler(TextView textView)
{
super();
this.textView = new WeakReference<>(textView);
}
上面代码是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四种方式,我们基本了解了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小试一下。
代码的思路是么有问题的,主线程和子线程先沟通,然后子线程有结果以后再和主线程沟通,这个逻辑没有问题,但是我们run的时候提示在L66 有个空指针异常,这个问题是由于多线程并非引起的,因为这个looper还没有来得及初始化,所以报了个空指针异常,那么Google给我们提供的HandlerThread就做了同步处理,所以如果将我们自己写的带有Looper的子线程换成HandlerThread就不会有这个多线程并发引起的问题。
上面已经基本回答了主线程和子线程之间如何相互通信,一般建议使用HandlerThread,如果不乐意直接使用,也可以自己定义一个带有Looper的Thread的,但是必须处理好多线程并发问题。另外,对于Handler的构造器,其中有个参数是Callback,这个Callback作用是什么?看看源码一切皆清楚了
很明显的L35行,如果这个callback返回的是true 就不会继续往下执行了,所以这个callback有截获的作用。
关于Handler的介绍到这里基本结束了,当然Handler的其他知识还有不少,这里不在赘述。