目录
学习目录1. Handler的作用
-
简单说Handler用于同一个进程的线程之间通信。(可以是不同线程,也可以是同一个线程,比如像做延时操作)
-
基本原理是,Handler发送Message,并放入对应线程的MessageQueue中,Looper让对应线程无限循环地从自己的MessageQueue拿出消息处理。(Handler和Looper持有的是同一个MessageQueue)
-
使用的最多的场景就是,我们在UI线程创建好Handler实例,然后在子线程做完耗时操作后,想要更新UI内容时,通过mHander sendMessage通知UI更新。而正如1所说,理论上我们也完全可以由UI线程发送Message,由子线程接收并处理,只是比较少见。另一个多见的场景是延时任务,往往是UI线程自己跟自己通信。
2. 为什么需要Handler?
-
一般来说,我们只要在子线程把信息放进主线程的MessageQueue里就可以了。因为,在同一进程中线程和线程之间资源是共享的,也就是对于任何变量在任何线程都是可以访问和修改的,只要考虑并发性做好同步就行了,那么只要拿到主线程的MessageQueue 的实例,就可以放入消息,主线程的Looper在轮询MessageQueue时,就可以取出该消息并处理。
-
主线程的MessageQueue的实例是可以拿到的(在主线程下 Looper.myLooper().mQueue),但是Google 为了统一添加消息和消息的回调处理,又专门构建了Handler类.只要在主线程构建Handler类,那么这个Handler实例就获取主线程MessageQueue实例的引用,Handler 在sendMessage的时候就通过这个引用往消息队列里插入新消息。
-
Handler 的另外一个作用,就是能统一处理消息的回调。这样一个Handler发出消息又确保消息处理也是自己来做,这样的设计非常的赞。具体做法就是在队列里面的Message持有Handler的引用(哪个handler 把它放到队列里,message就持有了这个handler的引用),然后等到主线程轮询到这个message的时候,就来回调我们经常重写的Handler的handleMessage(Message msg)方法。
// Looper.loop轮询方法
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 获取MessageQueue
final MessageQueue queue = me.mQueue;
// 省略
// 具体的轮询逻辑,无限for循环
for (;;) {
// 取出Message
Message msg = queue.next(); // might block
// 省略
try {
// target为发送message的Handler实例
// Handler处理
msg.target.dispatchMessage(msg);
}
// 省略
}
}
所以说,引入Handler只是为了大家使用方便以及代码的清晰简洁。并没有大家想的那么高深。
3. 具体的使用
3.1 主线程使用Handler刷新UI
Handler handler = new Handler()
实际会调用
public Handler(Callback callback, boolean async) {
// 省略
// 这里也验证了,Handler在哪个线程创建,他就会持有哪个线程的Looper
// 我们一般在UI线程初始化,Handler就会持有UI线程的Looper和MessageQueue
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
// 这里也验证了,Handler在哪个线程创建,他就会持有哪个线程的MessageQueue
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
在其他线程创建想在主线程处理事情的Handler,可以用以下代码可以达到相应效果
// 传入UI线程的Looper
Handler handler = new Handler(Looper.getMainLooper);
3.2 LooperThread子线程使用(官网的文档)
class LooperThread extends Thread {
//其他线程可以通过mHandler这个引用给该线程的消息队列添加消息
public Handler mHandler;
public void run() {
Looper.prepare();
//需要在线程进入死循环之前,创建一个Handler实例供外界线程给自己发消息
mHandler = new Handler() {
public void handleMessage(Message msg) {
//Handler 对象在这个线程构建,那么handleMessage的方法就在这个线程执行
}
};
// loop方法里会用到Handler实例
// 所以必须先初始化Handler
// 如果在loop方法之后初始化Handler,那么loop方法执行中会报错
Looper.loop();
// loop之后才初始化Handler,代码是无效的,loop是死循环,正常情况下这行代码就不会执行了
// mHandler = new Handler()......
}
}
需要说明的是,上面写到的Looper.prepare,创建Handler和Looper.loop方法的顺序并不一定不能改。如果你想的话,也完全可以loop执行之后创建Handler,只是创建的流程不能写在loop后面。因为loop里的死循环会导致你的代码不执行,你可以在主线程通过LooperThread.mHander这样的引用,来创建实例,效果也是一样的。
注意,其实UI线程也有类似的代码,如下:
public final class ActivityThread {
public static final void main(String[] args) {
......
Looper.prepareMainLooper();
......
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
......
Looper.loop();
......
}
}
与上面的例子类似,系统在这里也为我们初始化了一个Handler。我们每次使用Handler mHandler = new Handler();都是额外创建了一个Handler,与原有的不冲突。msg.target.dispatchMessage(msg)这句代码会判断target。
4.Handler发送消息的两种方式
Handler,它直接继承自Object,一个Handler允许发送和处理Runnable或者Message对象,并且会关联到主线程的MessageQueue中。所以Handler把消息压入MessageQueue也有两种方式,post(new Runnable)和sendMessage(Message msg)。
4.1 post
post允许把一个Runnable对象入队到消息队列中。它的方法有:
- post(Runnable)
- postAtTime(Runnable,long)
- postDelayed(Runnable,long)。
4.2 sendMessage
sendMessage允许把一个包含消息数据的Message对象压入到消息队列中。它的方法有:
- sendEmptyMessage(int)
- sendMessage(Message)
- sendMessageAtTime(Message,long)
- sendMessageDelayed(Message,long)。
从上面的各种方法可以看出,不管是post还是sendMessage都具有多种方法,它们可以设定Runnable对象和Message对象被入队到消息队列中,是立即执行还是延迟执行。
4.3 post和sendMessage方法的联系和区别
先看源码
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
代码很好理解,post方法其实还是把Runnable对象转化成了Message,区别在于普通的sendMessage不会使用callBack参数,它具体的处理逻辑在Handler的handleMessage里。而post会使用Message的callback,callback就是Runnable对象,所以使用post方法的话,无需再去写具体的handleMessage逻辑。源码如下:
// dispatchMessage方法是在Looper.loop开启循环,开始处理MessageQueue里的每个Message时调用的,可以发现,默认先调用callback,没有callback才会使用handleMessage
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
5. 了解Message
Message是一个final类,所以不可被继承。Message封装了线程中传递的消息,如果对于一般的数据,Message提供了getData()和setData()方法来获取与设置数据,其中操作的数据是一个Bundle对象,这个Bundle对象提供一系列的getXxx()和setXxx()方法用于传递基本数据类型的键值对,对于基本数据类型,使用起来很简单,这里不再详细讲解。而对于复杂的数据类型,如一个对象的传递就要相对复杂一些。在Bundle中提供了两个方法,专门用来传递对象的,但是这两个方法也有相应的限制,需要实现特定的接口,当然,一些Android自带的类,其实已经实现了这两个接口中的某一个,可以直接使用。方法如下:
putParcelable(String key,Parcelable value):需要传递的对象类实现Parcelable接口。
pubSerializable(String key,Serializable value):需要传递的对象类实现Serializable接口。
还有另外一种方式在Message中传递对象,那就是使用Message自带的obj属性传值,它是一个Object类型,所以可以传递任意类型的对象,Message自带的有如下几个属性:
int arg1:参数一,用于传递不复杂的数据,复杂数据使用setData()传递。
int arg2:参数二,用于传递不复杂的数据,复杂数据使用setData()传递。
Object obj:传递一个任意的对象。
int what:定义的消息码,一般用于设定消息的标志。
注意
对于Message对象,一般并不推荐直接使用它的构造方法得到,而是建议通过使用Message.obtain()这个静态的方法或者Handler.obtainMessage()获取。Message.obtain()会从消息池中获取一个Message对象,如果消息池中是空的,才会使用构造方法实例化一个新Message,这样有利于消息资源的利用。并不需要担心消息池中的消息过多,它是有上限的,上限为10个。Handler.obtainMessage()具有多个重载方法,如果查看源码,会发现其实Handler.obtainMessage()在内部也是调用的Message.obtain()。
网友评论