Android中的消息机制
来源:互联网 发布:网络分析仪器 编辑:程序博客网 时间:2024/06/09 14:10
Android中的消息机制 - 密切协作的 Handler,Looper,MessageQueue
1.消息机制概述
知其然,知其所以然 ,既然说到消息机制,那么我们就要弄明白了,为什么需要用到handle机制?
我们知道,Android中的消息机制主要用来更新ui操作 ,可是为什么更新ui操作一定要放到消息机制来实现呢?
void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); } }我们知道在更新ui的时候,程序会去检查当前线程是否是当前线程,也就是 ui 线程,否则就会抛出异常。
至于为什么google的Android工程师不允许我们再子线程中更新ui呢,那是因为Android中的ui操作不是线程安全的,如果在多线程下去操作ui而不做任何保护措施,那么就会导致 ui 操作导致不可预期的效果 。
如何解决这种并发的操作ui的问题,1,可以通过加锁,加锁的方式虽然也能够实现,但是使用枷锁却又两个问题存在
a.加锁会降低ui的访问效率
b.会让ui操作的逻辑变得复杂
大概是基于这种考虑吧(个人猜测) ,Android 系统工程师 在 ui的更新上,采取了 handler 的机制
2.Android中的消息机制实现
提到Android中的消息机制,不可避免的就要提到handler ,handler作为系统提供给我们使用的上层接口,是我们操作ui的主要交互对象。
2.1.handler的用法
Handler handler = new Handler(){public void handleMessage(Message msg) {switch (msg.what) {case 1://执行更新ui操作break;}}};new Thread(){public void run() {//执行复杂的计算逻辑Message obtain = Message.obtain();obtain.what = 1;obtain.obj = "更新ui";handler.sendMessage(obtain);};}.start();如上,当我们在子线程中计算一个复杂的操作,然后需要更新ui的时候,那么我们可以通过handler的handleMessage方法来更新ui操作,两者密切配合,完成更新操作。
2.2.在子线程中使用handler
我们看下面这个代码
new Thread(){public void run() {Handler handler = new Handler();};}.start();当子线程的run方法执行的时候,我们满怀期待的去new 一个handler 突然就会发现,what a fuck ,竟然报错了
通过异常信息发现我们没有调用 looper.prepare() ,这是什么鬼 ,通过观察源码发现,
我们接着看 myLooper()的实现
sThreadLocal 是什么 ? ThreadLocal ,我们暂且不管,现在从 sThreadLocal 中取不到looper实例,那么既然 异常信息提示我们调用 Looper.prepare() 我们暂且去看看,
噢,原来是在这里。那我们现在尝试 去调用吧 。
new Thread(){public void run() {Looper.prepare();Handler handler = new Handler();};}.start();好了,到此为止,我们终于可以在子线程中 创建一个handler实例了,注意,这里我说的是创建实例,并不代表这样就可以正常使用了。
2.3.子线程中handler的正确使用姿势
以上就是在子线程中创建handler的正确姿势,我们发现 多了一行 Looper.loop() 方法,首先我们看下他的实现
通过looper的代码实现我们可以发现,looper.loop()方法 通过不断的循环,不停的从messageQueue中获取message 进行分发,最终 通过 handler的 dispatchMessage 分发出去我们的消息
我们通过重写handler的 handMessage方法实现自己的业务逻辑
3.Handler,Looper,MessageQueue 之间协作关系和工作流程
图片来源:http://gityuan.com/2015/12/26/handler-message-framework/
以上就是 handler通信机制的流程图,感谢 gitYuan 的图片。
一次handler通信的过程可以分为如下几个步骤
1.handler 通过sendMessage 方法 发出一个message对象,然后将message对象加入内部的MessageQueue中(链表实现)
2.Looper.looper() 通过不停的循环,处理消息 ,然后将message分发出去交给对应的handler处理
3.使用者通过重写handleMessage 方法处理业务逻辑
4.关于threadLocal
threadLocal一个泛型类,通过threadLocal我们可以在指定的线程中保存数据。他可以实现线程隔离,分别为不同的线程保存数据副本,做到互不干扰。
4.1 threadLocal在handler中的使用
Looper 类 中维护着一个变量数据
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();sThreadLocal 为每一个线程保存了一份looper对象实例
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread();}同时呢,looper又是一个私有的构造方法,也就意外着我们不能够在外部显示的构造looper实例。在上面的例子中,我们通过looper.prepare()在线程中使用handler,
下面是prepare的方法实现
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)); }通过sThreadLocal去获取一个looper对象,如果looper不为空抛出异常(也就说明,每一个线程中只能存在一个looper对象),否则呢 就新增一个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) return (T)e.value; } return setInitialValue(); }首先会去获取当前线程的ThreadLocalMap 对象,这个对象中维护了一个Entry数组,entry中有维护了value 对象, 也就是 我们的 looper对象。
5.关于handler的认识和理解
1.每个线程中只能有一个looper对象
2.子线程中使用handler必须首先创建looper
3.一个线程中可以有多个handler对象,message对象维护了分发的handler对象实例,所以多个handler的分发不会有问题
4.threadLocal机制保证了每个线程内部只会维护了一个looper对象
问题: 既然looper维护了一个无限循环的队列,为什么主线程不会因此而假死或者卡死呢 ? 同样求解
6.参考
1. gityuan: Android消息机制1
2.《Android开发艺术探索》
3. handler,looper,message,threadLocal源码阅读全文
0 0
- Android 中的消息机制
- Android中的消息机制
- Android中的消息机制
- Android中的消息机制
- Android中的消息机制
- Android中的消息机制
- Android中的消息机制
- Android中的消息机制
- Android中的消息机制
- Android中的消息机制
- Android中的消息机制
- Android中的消息机制
- Android中的消息机制
- Android中的消息机制
- Android中的消息机制
- Android中的消息机制
- Android中的消息机制
- Android中的消息机制
- mybatis $ # 区别
- Android 布局中如何使控件居中
- NSTimer封装使用
- 写一个简单的颜色选择插件
- BZOJ 3994: [SDOI2015]约数个数和 莫比乌斯反演
- Android中的消息机制
- Maven初始
- 深入浅出: 大小端模式
- HDU 1114 Piggy-Bank(DP,水题)
- 【python 爬虫】身份证归属地爬虫
- Java中==和equals的异同以及hahCode
- spring +Springmvc+ hibernate
- Xcode8.3 加载WebView时报错
- Danganronpa