因为android的UI线程不是线程安全的,所以更新视图只能在主线程更新,那么如果我的子线程想要更新UI该怎么办呢?这就是 Handler出现的原因了,虽然我们通常将Handler用在子线程需要更新UI的场景下,也可以用来传递消息。
一、角色分析
1、Handler:处理器,负责的内容是消息的发送和具体处理流程,一般使用时由开发者重写handleMessage函数,根据自定义的不同message做不同的UI更新操作;
2、Message:消息对象,代表一个消息实体,存放消息的基本内容; 创建时尽量使用Message.obtain(),复用Message
3、MessageQueue:消息队列,按顺序存放消息实体,由单链表实现,被Looper所使用(一个Looper具有一个消息队列);
4、Looper:循环器,也是我们的消息机制的主角,一个线程至多具有一个并且与线程一一对应(如何理解等会来说),负责的内容是不断从消息队列取出放入的消息并且交由消息对应的Handler进行处理(Handler的具体handleMessage操作);
5、ThreadLocal:线程本地数据,是一个线程内部的数据存储类,为每个线程存储属于自己的独立的数据。
二、总览
public class Activity extends android.app.Activity {
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
System.out.println(msg.what);
}
};
@Override
public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
............耗时操作
Message message = Message.obtain();
message.what = 1;
mHandler.sendMessage(message);
}
}).start();
}
}
消息机制的运行流程:
handler发送消息到messageQueue中,MessageQueue会按时间排列Message,looper会不断检查是否该执行message中的方法,到时间就取出来让相应的handler去执行。
MessageQueue,Handler和Looper三者之间的关系:每个线程中只能存在一个Looper,Looper是保存在ThreadLocal中的。主线程(UI线程)已经创建了一个Looper,所以在主线程中不需要再创建Looper,但是在其他线程中需要创建Looper。每个线程中可以有多个Handler,即一个Looper可以处理来自多个Handler的消息。 Looper中维护一个MessageQueue,来维护消息队列,消息队列中的Message可以来自不同的Handler。
三、源码解析
1.Looper
要想使用消息机制,首先要创建一个Looper。
初始化Looper
无参情况下,默认调用prepare(true);表示的是这个Looper可以退出,而对于false的情况则表示当前Looper不可以退出。
子线程初始化looper应当使用默认方法或者传入true参数,防止无法退出
public static void prepare() {
prepare(true);
}
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));
}
这里看出,不能重复创建Looper,只能创建一个。创建Looper,并保存在ThreadLocal。其中ThreadLocal是线程本地存储区(Thread Local Storage,简称为TLS),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的TLS区域。
开启Looper
public static void loop() {
final Looper me = myLooper(); //获取TLS存储的Looper对象
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}