Looper和Handler —— Android 消息驱动机制
Android 与 Windows 系统一样也是消息驱动型系统。
消息驱动机制四要素
- 接收消息的“消息队列”
- 阻塞式地从消息队列中接收消息并进行处理的“线程”
- 可发送的“消息的格式”
- “消息发送函数”
Android 对消息驱动机制的对应方式
- 接收消息的“消息队列”—— MessageQueue
- 阻塞式地从消息队列中接收消息并进行处理的“线程”—— Thread + Looper
- 可发送的“消息的格式”—— Message
- “消息发送函数”—— Handler 的 post 和 sendMessage
部分名词说明:
消息队列:通过 Handler 发送的消息并非马上执行的,需要加入队列中进行维护和处理。
工作线程:需要一个线程不停取出消息,执行回调,这就是 Looper 线程。
互斥机制:出现不同的线程向同一个消息队列插入消息时,需要采用同步机制保证不出错。
APP 端 UI 线程都是 Looper 线程,每个 Looper 线程维护一个消息队列。
其它线程(Binder、自定义线程等)都能通过 Handler 对象向 Handler 所依附的消息队列线程发送消息。
发送 Message 就是通过 Handler 向其的 MessageQueue 插入消息。
APP 端使用 Handler
在 MainActivity 中使用 Handler:
{
private Handler mHandler;
public static final int UI_HANDLER_CODE_START = 1765499380;
public static final int UI_HANDLER_CODE_TOAST_MSG = UI_HANDLER_CODE_START + 1;
public static final int UI_HANDLER_CODE_LOGD_MSG = UI_HANDLER_CODE_START + 2;
...
public static Handler getUiHandler() {
return handler;
}
mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case UI_HANDLER_CODE_START:
break;
case UI_HANDLER_CODE_TOAST_MSG:
Toast.makeText(MainActivity.this, "UI_HANDLER_CODE_TOAST_MSG 消息", Toast.LENGTH_SHORT).show();
break;
case UI_HANDLER_CODE_LOGD_MSG:
Log.d(TAG, "UI_HANDLER_CODE_LOGD_MSG 消息");
break;
}
}
};
}
其它线程可以通过发送特定的消息,让主线程处理:
MainActivity.getUiHandler().sendEmptyMessage(MainActivity.UI_HANDLER_CODE_TOAST_MSG);
也可以通过其它 send 接口,如 sendMessage 接口发送 Message 数据到 Handler 中处理。
其它线程下使用 Handler
其它线程下是不是能直接 new Handler 然后操作呢?不可。
因为 Handler 只能在 Looper 线程中才能创建。
在 APP 端默认的 UI 线程即是 Looper 线程,所以可以创建。
但是普通的线程可不是 Looper 线程。
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;
}
Looper 线程的判断用 Looper.myLooper()
返回值(获取当前绑定的 Looper 线程)来判断。
如果不存在 Looper 线程,需要调用 Looper.prepare()
来创建。(每个线程只能有一个 Looper 线程单例,重复创建会报错)
下面以传递字符串实现 Toast 弹窗显示的函数为例:
private static void showToast(Context context, final String text) {
Looper looper = Looper.myLooper();
if (looper == null) {
Looper.prepare();
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Toast.makeText(context, text, Toast.LENGTH_SHORT).show();
}
};
mHandler.sendEmptyMessageDelayed(0, 0);
Looper.loop();
} else {
// 处于主线程内
mHandler = new Handler(looper) {
@Override
public void handleMessage(Message msg) {
Toast.makeText(context, text, Toast.LENGTH_SHORT).show();
}
};
mHandler.sendEmptyMessageDelayed(0, 0);
}
}
HandlerThread 和 AsyncTask
HandlerThread 是对 Thread 和 Handler 的封装,实际上是 Thread 和 Looper 进行了绑定。
AsyncTask 则进行了进一步的封装,完全隐藏了 Thread 和 Handler 的概念,采用 doInBackground(Params... params)
进行处理。
但是这个效率不高而且资源占用较大,只适合少量的操作。
网友评论