美文网首页
Looper和Handler —— Android 消息驱动机制

Looper和Handler —— Android 消息驱动机制

作者: ZONE画派 | 来源:发表于2019-01-14 19:15 被阅读12次

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) 进行处理。
但是这个效率不高而且资源占用较大,只适合少量的操作。

相关文章

网友评论

      本文标题:Looper和Handler —— Android 消息驱动机制

      本文链接:https://www.haomeiwen.com/subject/ikwjdqtx.html