use for
相信于此,绝大多数同学都会回答消息机制是android 为了线程间通信而引入的工具。可以轻松的将一个任务切换到handler所在线程执行。android开发规范有规定,不允许于子线程更新ui,这样会触发异常;我们平时使用handler主要都是将子线程切换到主线程中去执行;因此从本质上来来说,Handler并不是专门用于更新UI的,它只是常被开发者用来更新UI。
Q?为何不能在主线程外更新ui呢?
A:因为Android的UI线程是非线程安全
的,应用更新UI,是调用invalidate()
方法来实现界面的重绘,而invalidate()
方法是非线程安全的,也就是说当我们在非UI线程来更新UI时,可能会有其他的线程或UI线程也在更新UI,这就会导致界面更新的不同步。因此我们不能在非UI主线程中做更新UI的操作。也就是说我们在使用Android中的线程时,要保证: 更新ui都在UI主线程执行.
Q: 那为何不将需要更新ui的操作放在UI线程执行呢?
**A:**我们都知道在java中,线程存在以下几种基本状态:创建
,就绪
,运行
,阻塞
,死亡
。我们的应用启动后,所有的交互都是在UI
线程完成的;如果在UI
执行延时操作,如常见的网络请求
,UI
线程就会进入阻塞
状态;此时用户就无法响应任何操作了;如果此过程超过5秒,就会让程序处于ANR(application not response)
,这时用户就可能想要和你的应用说声gg
了。
Q :Android提供了哪几种线程间通信方式?
A:AsyncTask?
,Handler
。为什么AsynTask
打了个?
呢,我们可以简单看下AsynTask
源码,他内部也是接住handler来进行线程间通信的。
Q:messageQueue****可以取没有Targer对象的消息,那和我们正常流程中,消息来源是不是有出入?
A : 其实平时我们使用的Message
,都是通过Handler
发送的,有一些系统消息,他们会直接通过调用MessageQueue
发送一个屏障消息,这类消息没有Target
,然后配合Handler
发送异步消息来使用;当MessageQueue
读取到屏障消息后,他们会直接在链表中找到最近的异步消息
,直接执行。
feature-要素
· Message(消息单元)
定义一个可以发送到Handler
的消息;它定义了消息Id
,两个额为的int字段和一个额外的object字段
(消息处理对象),它们可以不被初始化;虽然它的构造方法是public,但是还是建议我们通过obtain系列函数进行定义。
· MessageQueue(消息队列)
存放所有发送的消息队列,单链表结构,供Looper从中读取数据;延时消息是怎么存取的,这个很有趣;
· Looper(消息读取者)
永动机;其中有个死循环函数Loop()
,不断读取MessageQueue
中的消息,交给目标处理;问题来了,既然是个死循环,那不是始终会阻塞Looper
所在线程吗。这又是如何解决的。
· Handler(消息分发以及处理者)
通过sendMessage
系列函数,会将Message
传入MessageQueue
中;Looper.loop()
读取到消息传递给Handler
处理。
desc
-
handler创建前,Looper.loop()执行前;需要保证当前线程Looper有创建,而这个保证即Looper.prepare();主线程由于在ActivityThread创建时,已经做过,所以无需执行;
-
Looper.loop()中有一个死循环,所以线程资源不会释放;
MessageQueue
中的quit
函数,我们才能释放资源; -
Java
中,所有非静态成员变量会持有当前对象的引用(不然你又是怎么引用外部类的各种成员变量和函数等);那样我们在Activity
中通过new Handler()
, 创建的对象会持有当前页面的引用;而我们发送的每个消息不能保证是立即执行,以及迅速执行结束的,handler.sendEmptyMessageDelayed
;消息是会持有handler
做为他的target
,那在这个message
在通过msg.target.dispatchMessage(msg);
会一直被持有;这样会导致messageQueue->message->handler->activity|fragment
;在页面被销毁,声明周期执行到desatory
时,activity
不会得到释放,从而内存泄漏;handler
得到消息处理时,如果当前页面已经被销毁,执行Ui
更新,又会导致难以预料的问题。 -
针对
3
所提的我们可以按以下两种处理:1:页面destory
销毁时,调用handler.removeCallbacksAndMessages(null);
2:通过软引用创建静态Handler对象;
流程解析
android handler流程分析晚上有很多资料;我们这儿简单介绍下:
· Looper
,MessageQueue
就绪;调用Looper.prepare()
,其间会向Looper
静态线程变量sThreadLocal
插入一个当前线程的Looper
;在调用Looper
构造函数时,我们会初始化MessageQueue
,并将mThread
设置为当前Thread.currentThread();
· Looper.prepare()
代码块如下:
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
//sThreadLocal->ThreadLocal对象,里面封装了一个map逻辑,key是线程hash值;static 类变量
if (sThreadLocal.get() != null) {//不允许多次 prepare
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));//设置当前线程的Looper
}
Looper
构造函数,以及MessageQueue
构造函数如下:
private Looper(boolean