理解Android核心:Looper, Handler和HandlerThread

原文地址:https://blog.mindorks.com/android-core-looper-handler-and-handlerthread-bd54d69fe91a

这篇文章会讲解Android Looper, Handler和HandlerThread。它们都是构成Android系统的基石。

依据我个人的经验,直到最近我才开始使用它们。我使用它们的情形是向主线程/UI线程发任务,主要由于其它线程要更新UI。其它处理多线程操作的替代方式包括ThreadPoolExecutor, IntentService和AsyncTask。

多线程和任务的运行已经是老知识了。Java自身就有java.util.concurrent包里的Fork/Join框架来帮助实现。几个库已经实现了异步流水线处理。RxJava就是当今最热门的响应性编程,专门用来处理异步应用。

既然如此,那我为什么还要写这老旧的东西呢?

LooperHandler,和HandlerThread都是Android处理异步任务的方式。它们并非老旧的知识,而是Android那复杂的框架所依赖的骨架。

对于初级开发者,十分推荐明白它们背后的原理而有经验的开发者也应该复习这个知识以便重找拾细节。

使用案例:

  1. LooperHandlers伴随着Android中的主线程生成。因此,明白它们对于构建不阻塞响应的UI是十分必要的。
  2. 开发者开发第三方库时因为库大小的原因而不能再集成其它第三方的库。因此,对于库开发者而言,最好的选择便是最大限度地使用已有的资源。通过自己的方式处理异步任务并非能达到最有效和最优化的程度。
  3. 对于公司和个人开发者开放的SDK而言同样存在上面的问题。客户可能有各种可样的实现,但他们都具有相同的android 架构 API。
  4. 明白它们将会增强与Android SDK和一般性类的兼容性。

让我们开始带着问题探索/复习

我希望每个读者都有对Java线程最基本的认知。如果有必要,可以快速查阅Java的Thread和Runnable。

Java的线程有什么问题?

Java线程是一次性的,在执行完后便结束了。

我们能够改善它吗?

线程是一把双刃剑。我们能够分解任务并通过它加速执行速度,但也会在线程数过多而拖慢速度。创建线程自身也会有消耗。因此,最优是保有一个优化数量的线程并重用它们完成任务的执行。

可重用性的模型

  1. 通过循环执行run()方法,线程一直处理活动状态。
  2. 负责任务执行线程是串行的,并通过一个队列(MessageQueue)来保持这种串行。
  3. 当完成后线程必须结束。

Android是如何实现的

以上模型在Android中是通过LooperHandler, 和HandlerThread。这个系统可以认为是文章封面中的车那样进行处理。

  1. MessageQueue是一个包含应该被执行的消息的队列。
  2. Handler通过LooperMessageQueue中的任务进队,同时在任务在MessageQueue出队后负责执行这些任务。
  3. Looper负责保持线程存活,不断轮询MessageQueue并将消息改善给相应的handler进行处理。
  4. 最终,当Looper的quit()执行后,线程退出。

每个线程只能有一个Looper而可以有许多不同的Handler和它关联。

为线程创建Looper和MessageQueue

线程创建后通过调用Looper.prepare()可以得到一个LoooperMessageQueueLooper.prepare()标识调用它的线程,创建一个LooperMessageQueue并在ThreadLocal存储类中将这个线程进行关联。Looper.loop()必须要在关联的线程中调用。类似的,looper必须通过looper.quit()显式地结束。

class LooperThread extends Thread {
     public Handler mHandler; 

     public void run() { 
         Looper.prepare();

         mHandler = new Handler() { 
             public void handleMessage(Message msg) { 
                // process incoming messages here
                // this will run in non-ui/background thread
             } 
         }; 

         Looper.loop();
     } 
}

为线程创建Handler

Handler隐式地与实例它的线程关联,但我们也可以通过在Handler的构造函数中传入looper来显式地绑定一个线程。

handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        // process incoming messages here
        // this will run in the thread, which instantiates it
    }
};

通过HandlerMessageQueue发送消息有两种模式。
1. Message:该类定义许多有用的方法来处理消息数据。我们通过obj 变量来发送object。

 Message msg = new Message();
msg.obj = "Ali send message";
handler.sendMessage(msg);

更多关于Message这个类的信息请查看:https://developer.android.com/reference/android/os/Message.html

  1. Runnable:一个runnable能够给推到MessageQueue。例如,推送一个任务到主线程中执行。
new Handler(Looper.getMainLooper()).post(new Runnable() {
    @Override
    public void run() {
        // this will run in the main thread
    }
});

上面的例子上,我们创建了一个Handler并提供了一个主线程的Looper与其进行关联。这样可以将这个handler与主线关联在一起。当我们推Runnabler时,它会进行到主线程的MessageQueue,然后在主线程中得到执行。

Handler能够以许多不同的方式处理消息,具体可查看:https://developer.android.com/reference/android/os/Handler.html

创建一个自己的线程并提供LooperMessageQueue并不是处理这类问题的正确选择。因此,Android提供了HandlerThread(Thread的继承类)来简化处理。内部上它和我们之前的处理方式是相同的,只不过更加健壮。因此,尽可能使用HandlerThread

private class MyHandlerThread extends HandlerThread {

    Handler handler;

    public MyHandlerThread(String name) {
        super(name);
    }

    @Override
    protected void onLooperPrepared() {
        handler = new Handler(getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                // process incoming messages here
                // this will run in non-ui/background thread
            }
        };
    }
}

注意:我们在onLooperPrepared()被调用时实例化Handler。因此,handler会与这个handlerthread的Looper进行关联。

  1. Looper只在HandlerThread的start()被调用后才准备好,例如当这个线程运行后。
  2. 一个Handler与一个HandlerThread进行关联仅在这个HandlerThreadLooper准备好了之后。

其它方式创建HandlerThread:

HandlerThread handlerThread = new HandlerThread("MyHandlerThread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());

注意: HandlerThread需要调用myHandlerThread.quit()来退出以便释放资源并停止线程的执行。

我希望你能够练习以上的代码,以便你能获得更多的细节点。

我已经创建了一个模拟邮局的例子来演示。例子如下:https://github.com/MindorksOpenSource/post-office-simulator-looper-example


你可能感兴趣的:(Android,Java,android,线程)