Android中的消息机制:Handler、Message
Hander、Message
Android 中的消息处理机制之一,也是我们经常使用到的一种;
其中牵扯到了 Handler、Message、MessageQueue、Looper等模块;
经典示例
public class HandlerActivity extends Activity {
private Handler handler = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 接受到消息,并处理.
switch (msg.what) {
case 1:
break;
default:
break;
}
}
};
// 创建子线程。
new Thread(new Runnable() {
@Override
public void run() {
// 子线程
handler.sendEmptyMessage(1);
}
}).start();
}
}
上面的代码比较简单
1、声明 Handler 对象,并实例化,重载 handleMessage(Message msg)
方法
2、创建子线程,并在子线程中通过 handler 对象发送消息
即可以实现线程间的消息传递;
为什么这样就可以达到消息的传递能力呢?
handler 分析
我们先来看下Handler中的一些代码:
// 一些变量
final Looper mLooper;
final MessageQueue mQueue;
// 构造方法.
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;
}
上面是Handler的一些变量以及构造方法,可以看到,Handler 持有一个 Looper 对象与 消息队列对象(mQueue),其中
- mQueue 是消息存储队列,职责:将消息有序的排列管理
- mLooper 是消息队列的执行人,职责:从消息队列中获取消息
Looper类分析
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
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));
}
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
如果大家看过源码的注释的话,可以看到:prepareMainLooper() 方法的执行是系统在启动应用前自动调用的;其在主线程(UI线程)执行,其中优先执行了 prepare(false) 方法,其目的是让当前线程持有一个 Looper 对象与 MessageQueue 对象,且主线程的 Looper与MesageQueue 不可退出;
经过上面的执行,将 Thread、Looper、MessageQueue相关联,但是并没有执行,需要继续执行 Looper.looper()方法
public static void loop() {
// ...
for (;;) {
// ...
}
}
该方法是开始执行死循环,不端的从 MessageQueue中取出 Message 对象并交由其 Handler 对象来处理(这个后面再说),这样以事件驱动的应用程序就跑起来了;
其中数据的校验也控制了一个 Thread 只能有一个 Looper、一个 MeesageQueue;
子线程
上面的代码是在主线程中创建的 Handelr,那我们是否可以在子线程中进行同样的操作呢?
执行代码:
new Thread(new Runnable() {
Handler childHandler;
@Override
public void run() {
childHandler = new Handler();
}
}).start();
执行之后大家会发现,异常了: RuntimeException("Can't create handler inside thread that has not called Looper.prepare()"
异常信息很明确:请先执行 Looper.prepare() 方法,实际上就是应为我们创建的线程没有Looper、MessageQueue等相关对象;上面说了,主线程(UI线程)是自动的在应用启动时帮我们进行了这些操作;而我自己创建的子线程默认是没有Looper、MessagwQueue的,所以需要我们显式的执行;并且,需要执行 Looper.looper()让 消息机制启动起来;
所以需要改正为:
new Thread(new Runnable() {
Handler childHandler;
@Override
public void run() {
// 将 Looper 与线程绑定
Looper.prepare();
childHandler = new Handler();
// 将当前线程的消息机制启动起来
Looper.looper();
}
}).start();
Message
在Andorid的消息机制中,Message扮演了重要的角色,是数据信息的搬运工,其承担了消息机制中数据的携带工作;
private static final int MAX_POOL_SIZE = 50;
private static int sPoolSize = 0;
Message next;
private static Message sPool;
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();
}
消息机制是一个需要大量Message对象来处理、传递信息的,所以需要不停生成新的Message对象,而新生成对象的开销是比较大的,尤其是需要频繁的生成、销毁;大家可能想到了对象池,是的,对象池可以很好的处理这种情况;但是看Message类的源码就会发现,这里并没有关于存储对象的Map集合,那又是如何实现的呢?
对象指向的是内存中的某一块地址;Message对象中包含了两个引用:sPool 和 next;而这里,sPool 实际指向了当前的 Message对象,而 next 指向的是 下一个 Message 对象 的地址;这样就组成了一个链表,变相的达到一个线程池的目的;当执行 ibtain()方法时,优先获取链表头的 Message 对象,边将可链表中可复用对象大小减一;
// 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使用过后,则将对象部分数据重置,放入链表头部,可复用对象数目加一;
这样就达到了复用的目的;
Message的传递以及消费
以文章开头的经典示例为例:
1. handler 在主线程定义
2. 在子线程通过 该 handler 发送消息;
这里消息的定义有多种,但是最终 Message 对象中的 target 的值都指向当前的 handler 对象;
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 都指向当前的 handler
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
这样,发送消息时的每一个消息都知道最终的消费者是谁。
之后调用native层的消息机制将消息放入到消息队列中;
public static void loop() {
// 省略代码...
for (;;) {
// 从消息队列中获取消息对象
Message msg = queue.next();
if (msg == null) {
return;
}
// ...
try {
// 分发,执行
msg.target.dispatchMessage(msg);
// ...
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
// ...
msg.recycleUnchecked();
}
}
上面的 Looper.looper()方法说明了,这是启动了死循环,不段的从线程的消息队列中读取消息;然后使用 target 所指向的 Handler 对象进行分发;最后调用 msg.recycleUnchecked() 方法回收掉已使用过的消息;
上一篇: 详谈Windows消息循环机制
推荐阅读
-
详解Android中的NestedScrolling机制带你玩转嵌套滑动
-
Android多线程处理机制中的Handler使用介绍
-
Android中实现异步任务机制的AsyncTask方式的使用讲解
-
Android中的多线程-Handler
-
iOS开发中实现hook消息机制的方法探究
-
详解Android ViewPager2中的缓存和复用机制
-
Android消息处理机制Looper和Handler详解
-
Android Handler Message 里面的message.what, message.arg1,message.obj,obtainMessage, message.setData的使用
-
Android Studio 之 Android消息机制 之简单Demo --- 使用Handler和Message类来完成消息传递
-
android 异步通信机制Handler的分析与运用