Handler 详解
2015-06-22 13:41
381 查看
这个详解基本小结了Handler的使用和相关的知识,在了解这些知识前,我们先来看看这些问题.
Handler 是什么?
子线程更新UI有几种典型方式,这些方式本质上是什么样的?
子线程真的不能更新UI吗?
HandlerThread是什么?
主线程和子线程之间如何相互通信?
好了,如果上面的问题,你能对答如流,并且深知其中的原理那么没有必要继续看下去了,反之,就该好好补补了(^o^)/~。
看完上面的解释,我们有个疑问,为什么要用Handler了?
因为android在设计的时候,就封装了一套消息创建、传递、处理机制,如果不遵循这样的机制就没有办法更新UI信息,就会抛出异常信息;
接下来我们就从源码的角度看一下Google是如何在线程中封装这么一套Handler机制的,我们先来找一下Java的入口函数Main方法。
在android.app.ActivityThread.java中。
接着我们重点关注 L4 L10 L20这三行代码,我们先一步一步跟进去
L4:
然后看看L88和L93
由上面的代码可以看到,一开始就初始化了一个Looper实例,然后将这个实例保存在sThreadLocal成员变量中,然后在需要的时候调用myLoop()方法,再把这个Looper实例取出来。所以很容易得到,一个线程对应唯一的Looper实例
L10:
在看第10行代码
然后我们看一下ActivityThread这个类的成员变量:
第27行,可以看到一个很奇怪的类 H 类,成员变量
接下来看看这个最精简的类:
是不是看到了我们熟悉的Activity的生命周期,对的,Activity的生命周期就是在这里触发的,所以最开始的L10
L20:
上面的代码首先取得Looper实例,然后取得该实例的
至此,UI线程的Looper准备完毕。
从Google给我们提供的SDK中我们大概知道有以下几种方式:
Handler Post
Handler SendMessage
RunOnUIThread
View Post
我们直接看一下上面四种交互方式的Demo:
这里特别补充一下L24添加父类构造器方法:
上面代码是java1.8引入Lambda表达式的写法。代码比较简单,这里不在赘述,我们接下来感兴趣的是,上面的四种交互方法的源码是神马样子的?
我们先看 updateViewByHandlerPost 第一种方式
上面代码需要注意的是L37行
接下来看updateViewByHandlerMessage
上面的代码需要注意的是L3,这里的Message实例有两种方式,分别是
接下来看updateViewByRunOnUiThread
如果不在UI线程,还是调用Handler的post
最后来看updateViewByViewPost
还是调用Handler的post,所以上面四种方式,本质上都是调用Handler的通信机制,所以在Android中子线程更新UI本质上都是通过Handler机制来处理。
运行以后,小伙伴都惊呆了,居然完美运行,这是神马情况?
我们先不直接回答这个问题,换个方式实现一下:
这样运行以后,是不是立马Crash了?并抛出
回答这个问题还是需要回到
注意L16行,
我们看看这个方法是弄啥咧。
是不是看到熟悉的异常出现在哪里了,以上的诡异事件是不是到此就弄清楚了,主要是ViewParent没有初始化完毕,所以不会执行UI线程检查,如果一旦初始化完毕,必然检查UI线程的更新操作,这个时候任何子线程企图更新UI的操作都无处遁形了。
代码的思路是么有问题的,主线程和子线程先沟通,然后子线程有结果以后再和主线程沟通,这个逻辑没有问题,但是我们run的时候提示在L66 有个空指针异常,这个问题是由于多线程并非引起的,因为这个looper还没有来得及初始化,所以报了个空指针异常,那么Google给我们提供的HandlerThread就做了同步处理,所以如果将我们自己写的带有Looper的子线程换成HandlerThread就不会有这个多线程并发引起的问题。
很明显的L35行,如果这个callback返回的是true 就不会继续往下执行了,所以这个callback有截获的作用。
关于Handler的介绍到这里基本结束了,当然Handler的其他知识还有不少,这里不在赘述。
Handler 是什么?
子线程更新UI有几种典型方式,这些方式本质上是什么样的?
子线程真的不能更新UI吗?
HandlerThread是什么?
主线程和子线程之间如何相互通信?
好了,如果上面的问题,你能对答如流,并且深知其中的原理那么没有必要继续看下去了,反之,就该好好补补了(^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的生命周期就是在这里触发的,所以最开始的L10
sMainThreadHandler = thread.getHandler();取回的Handler实例就是这么一个H类的实例。
[code]final Handler getHandler() { return mH; }
L20:
上面的代码首先取得Looper实例,然后取得该实例的
MessageQueue,然后死循环遍历这个queue。
至此,UI线程的Looper准备完毕。
子线程更新UI有几种典型方式,这些方式本质上是什么样的
既然Google为我们准备了Handler机制,所以我们在子线程中和主线程交互变得很简单了,那么子线程如何通过Handler和UI线程交互呢,又有哪些方法,这些交互的方式本质上是什么样子的呢,下面我们一起来探个究竟。从Google给我们提供的SDK中我们大概知道有以下几种方式:
Handler Post
Handler SendMessage
RunOnUIThread
View Post
我们直接看一下上面四种交互方式的Demo:
这里特别补充一下L24添加父类构造器方法:
[code]public UpdateViewHandler(TextView textView) { super(); this.textView = new WeakReference<>(textView); }
上面代码是java1.8引入Lambda表达式的写法。代码比较简单,这里不在赘述,我们接下来感兴趣的是,上面的四种交互方法的源码是神马样子的?
我们先看 updateViewByHandlerPost 第一种方式
上面代码需要注意的是L37行
msg.target = this;,这行代码说明,Handler发送消息总得有个接收方,而这个接收方不是别人,就是自己(Handler实例本身)。
接下来看updateViewByHandlerMessage
上面的代码需要注意的是L3,这里的Message实例有两种方式,分别是
Message message = Message.obtain();和
Message message = new Message();这两种方式哪种更好还是没有区别?为什么,自己想一想。
接下来看updateViewByRunOnUiThread
如果不在UI线程,还是调用Handler的post
最后来看updateViewByViewPost
还是调用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是什么
上面讲述的都是子线程主动和UI线程沟通,而一向高贵冷艳的UI线程从来没有主动和子线程打声招呼,是不是过于无礼了,那么主线程怎么样和子线程打招呼呢?这里Google很任性话的给我们提供了HandlerThread,这个类就是用来方便UI线程和子线程沟通的,那么我们可以不甩这个类吗,直接自己动手,丰衣足食如何,我们来首先不用HandlerThread小试一下。代码的思路是么有问题的,主线程和子线程先沟通,然后子线程有结果以后再和主线程沟通,这个逻辑没有问题,但是我们run的时候提示在L66 有个空指针异常,这个问题是由于多线程并非引起的,因为这个looper还没有来得及初始化,所以报了个空指针异常,那么Google给我们提供的HandlerThread就做了同步处理,所以如果将我们自己写的带有Looper的子线程换成HandlerThread就不会有这个多线程并发引起的问题。
主线程和子线程之间如何相互通信
上面已经基本回答了主线程和子线程之间如何相互通信,一般建议使用HandlerThread,如果不乐意直接使用,也可以自己定义一个带有Looper的Thread的,但是必须处理好多线程并发问题。另外,对于Handler的构造器,其中有个参数是Callback,这个Callback作用是什么?看看源码一切皆清楚了很明显的L35行,如果这个callback返回的是true 就不会继续往下执行了,所以这个callback有截获的作用。
关于Handler的介绍到这里基本结束了,当然Handler的其他知识还有不少,这里不在赘述。
相关文章推荐
- UVa 855 - Lunch in Grid City
- centos 安装 pear
- 日语学习之沪江N3基础 20150622 -4
- 简单理解动态内存分配和静态内存分配的区别
- hdu2422考研路茫茫——空调教室 tarjan+树形dp
- MD5加密字符串
- (ros/topic_tools): mux 话题的多路切换开关
- C++学习笔记__类的派生和多态性
- 开始刷leetcode day47: Word Break
- Yahoo!团队:网站性能优化的35条黄金守则
- 递归函数的经典例子(汉诺塔问题)
- poj1042贪心方法或者dp方法
- 4th Median of Two Sorted Arrays -- leetcode
- QT实现,通过URL下载文件的接口实现
- 微信公众平台开发:OAuth2.0网页授权
- 保护眼睛的桌面设置
- 5亿整数的大文件,怎么排?
- C++程序设计--对象分册(第5章)
- 搭建andriod开发环境
- 黑马day06 EL表达式之cout&cset&cremove标签