我们在使用线程切换时,经常会遇到下面这种代码的写法
final static int ONE_CODE = 1;
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.arg1){
case ONE_CODE:
Toast.makeText(getApplicationContext(), "arg1 : " + ONE_CODE +" obj : "+ msg.obj, Toast.LENGTH_LONG).show();
break;
}
}
};
void test(){
new Thread(new Runnable() {
@Override
public void run() {
Message msg = new Message(); // Message msg = Message.obtain();
msg.arg1 = ONE_CODE;
msg.obj = "one";
mHandler.sendMessage(msg);
}
}).start();
}
我们定义了 Handler,在它方法中接收数据,然后做UI的操作,test() 中,子线程做了耗时操作,然后把数据封装到 msg 中,通过 mHandler 来完成线程切换,我们注意看子线程中第一行代码, Message msg = new Message(),这是 new 一个对象,但是稍微有点经验的都会用后面的代码 Message msg = Message.obtain(),这里面有什么区别吗?我们看看 Message
public final class Message implements Parcelable {
public int what;
public int arg1;
public int arg2;
public Object obj;
public Messenger replyTo;
public int sendingUid = -1;
static final int FLAG_IN_USE = 1 << 0;
static final int FLAG_ASYNCHRONOUS = 1 << 1;
static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
int flags;
long when;
Bundle data;
Handler target;
Runnable callback;
Message next;
private static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
private static boolean gCheckRecycle = true;
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
void recycleUnchecked() {
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
}
Message 实现了 Parcelable 接口,说明是可以被序列化的,看看上面的成员变量, what、arg1、arg2 三个int的值,上面例子中,ONE_CODE 是个常量,我们可以用它作为 msg 的标签,在子线程中和 Handler 中形成映射关系,我们例子中用的是 arg1,实际上按照标准的写法,应该是用 what,它本来就是定义用拉力区分标识的,替换两行代码,msg.what =ONE_CODE; switch (msg.what){ ...} 即可。arg1 和 arg2 是我们用来传递 int 参数的,上面这样写,是为了说明,这几个值的作用,某些方面是相同的。如果要传递比较复杂的数据,使用 Object obj 或 Bundle data 这两个; Messenger replyTo 是用来跨进程通讯使用,前面讲 Messenger replyTo 这一章时提到过; int flags 是用来标识当前 Message 是否正在使用,上一章中 MessageQueue 中的 enqueueMessage(Message msg, long when) 方法中,msg.markInUse() 就改变了标识的值 void markInUse() {flags |= FLAG_IN_USE;};long when 是用来延迟执行的时间值;Handler target 是指此 msg 对应的 Handler;Runnable callback 是指 msg 对应的回调,一般默认为null,不会添加;下面重点来了,看看 Message next 及剩余的几个变量,这里牵涉到上面的问题,为什么使用 Message.obtain() 而非是 new Message()。private static final Object sPoolSync = new Object(),sPoolSync 是静态的,是所有 Message 对象共享的;我们看看 obtain() 方法,第一行代码就对 sPoolSync 使用synchronized 同步锁关键字,保持线程同步,然后对 sPool 进行判断,它也是静态,如果它值为null,则 new 一个 Message 返回;如果不为空,sPool 就是 Message 链表的 head 头部,这时候找到它的下一个,让 head 指向下一个 Message,同时切断它俩之间的链表链接,m.flags = 0 清空这个 Message 的正在使用的标识,然后返回。
我们看看 obtain() 的其它重载方法,最终都会调用到它,只不过是多了赋值。Message 是支持单链表链接的,所以才有了上面的方法,把它形成一个缓存,可以服用,上面的方法是获取缓存,那 Message 使用完毕后,是在哪里回收它形成缓存呢?我们看 recycleUnchecked() 方法。方法中是把一些成员变量制动会归零,看看同步锁里面的代码,先进行个数判断,如果 sPoolSize 小于 50,则把 sPool 赋值给当前 Message 的下一个,然后把当前 Message 变为 head 头部,同时数字 sPoolSize 加一。 如果有 Message A、B、C 三个消息,按照顺序都被调用了这个回收方法,那么调用 A 时,sPool 默认为 null,则 A 的 next 为null,sPool被赋值为 A,sPoolSize 值为1;B 调用时,B的 next 赋值为 A,sPool 赋值为 B,sPoolSize 值为2,此时链表为 B--A;C 调用回收方法,同理,则链表变为了 C--B--A。那么回收方法什么时候调用呢? Message 自身里面 recycle() 会调用, MessageQueue 消息队列中 removeMessages() 方法中会把那些删除的 Message ,也会有回收操作,Lopper 中 next() 方法
for循环结尾处,也有 msg.recycleUnchecked() 调用,说明每个 Message 只要执行完,或是等待执行但是被 remove 移除了,都会回收,这样我们使用 obtain() 方法就避免了不必要的浪费,调高效率。
接下来看看 Handler 的代码,Handler 构造方法,最终都会调用到两个方法
一:
public Handler(Callback callback, boolean async) {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
二:
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
开头的例子中,会调用方法一,mHandler中的 Looper.myLooper() 对应的是UI线程的Looper,所使用的线程队列自然也是UI线程的队列;回忆下前面 Looper 章节中,我们在子线程中创建了 Handler,没有提前调用 Looper.prepare() 方法,那么 Handler 构造中,mLooper 就为 null,就会抛出异常了,这个之前也讲到过,这里讲的更准确些。如果我们想在子线程中创建的 Handler 仍然可以刷新UI界面,可以做到吗?看看构造方法二就知道了,我们只需要在创建 Handler 时,把UI线程对应的 Looper 穿进去即可,
void createHandler(){
new Thread(new Runnable() {
@Override
public void run() {
mHandler = new Handler(Looper.getMainLooper());
}
}).start();
}
UI线程中的 Looper 在FrameWork层已经执行了 Looper.prepare() 和 Looper.loop(),所以我们在子线程中创建 Handler 不需要调用这两个方法了。
我们从 Looper 中知道,它从 MessageQueue 中抽取 Message,最终都调用了 Handler 的 dispatchMessage(msg) 方法,我们看看这个方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
public interface Callback {
public boolean handleMessage(Message msg);
}
Message 的 callback 是 Runnable 类型,这里会先判断msg的callback对象是否为null,不会null的话就执行它,如果为null,再判断 Handler 本身的 mCallback 属性是否为null,如果不为null,看看它的回调方法返回的值是否为true,如果为true,则到此为止,如果不为true,则会执行 handleMessage(msg) 方法;如果 mCallback 它本身就是为null,也会执行 handleMessage(msg) 方法,这个方法是需要我们重写的,就像上面的例子。
Handler 发送消息有几种方法,我们先说开头例子中的 sendMessage() 方法,例子中是不延迟马上执行,如果想延迟三秒执行,则可以调用 sendMessageDelayed() 方法,看看它的源码
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
前面几个方法都是发送消息的,可以立刻执行,也可以延迟执行或者定时执行,都是以开机时间为标准进行操作的,最终都会走到 enqueueMessage() 方法中,在这里,会把当前 Handler赋值给 Message 的 target 属性,然后添加的 MessageQueue 队列中,这里就和上一章中往消息队列中添加消息的方法对应上了,最终会走到 Handler 的回调中。
Handler 切换子线程还有一种方法,即 post()、postAtTime()、postDelayed() 方法,和上面的 sendMessage()几个方法相对应,用 post() 举例
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;
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
它经过一系列方法,最终也是走到了 enqueueMessage(queue, msg, uptimeMillis) 方法中,和上面的一样,这里注意一点,即 getPostMessage(Runnable r) 方法,在这个方法中,通过 Message.obtain() 创建了 Message 对象,然后把 runnable 对象赋值给 msg 的 callback 属性,所以通过 post() 系列方法执行的操作,最终也都到了Handler 的 dispatchMessage()方法,注意了,这时候 dispatchMessage(Message msg) 方法第一行代码 msg.callback != null 条件符合,所以就会执行 runnable 的 run() 方法,看到这也明白了,使用 Handler 的 post() 方法,仅仅是切换线程,runnable 还是在UI线程中执行,所以它里面时不能做耗时操作的,笔者刚开始学Android时就犯了这个错误,当时还不理解。
Handler 中移除消息的方法,removeCallbacks() 最终还是调用 MessageQueue 的 removeMessages() 方法,这个上一章分析过了。其他方法都是一些缓存复用方法,看看就好了,要灵活运用。Messenger 跨进程通讯时,提到使用 Messenger 发送信息,Messenger 中的 private final IMessenger mTarget 变量,对应的就是 Handler 中 MessengerImpl 对象,
class Messenger implements Parcelable {
private final IMessenger mTarget;
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
}
class Handler {
final IMessenger getIMessenger() {
synchronized (mQueue) {
if (mMessenger != null) {
return mMessenger;
}
mMessenger = new MessengerImpl();
return mMessenger;
}
}
private final class MessengerImpl extends IMessenger.Stub {
public void send(Message msg) {
msg.sendingUid = Binder.getCallingUid();
Handler.this.sendMessage(msg);
}
}
}
关于消息队列,应用层就基本讲完了,消息队列和 .aidl跨进程通讯都是 Android 的重点基础,学会他们有助于了Android架构的理解。