面试官:能说说HandlerThread的原理和使用场景吗?

4,089 阅读10分钟



初次看到HandlerThread的名字,我们可能会联想到HandlerThread这两个类,没错,它其实就是跟Handler和Thread有莫大的关系。HandlerThread继承自Thread,它本质上就是一个Thread,而且专门用来处理Handler的消息。

一、HandlerThread简介

看看官方对它的解释:

Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called.

大致就是说HandlerThread可以创建一个带有looper的线程,looper对象可以用于创建Handler类来进行来进行调度,而且start()方法必须被调用。

在Android开发中,不熟悉多线程开发的人一想到要使用线程,可能就用new Thread(){…}.start()这样的方式。实质上在只有单个耗时任务时用这种方式是可以的,但若是有多个耗时任务要串行执行呢?那不得要多次创建多次销毁线程,这样导致的代价是很耗系统资源,容易存在性能问题。那么,怎么解决呢?

我们可以只创建一个工作线程,然后在里面循环处理耗时任务,创建过程如下:

Handler mHandler;private void createWorkerThread() {  new Thread() {      @Override        public void run() {            super.run();            Looper.prepare();             mHandler = new Handler(Looper.myLooper()) {                @Override                public void handleMessage(Message msg) {                    ......                }            };            Looper.loop();        }    }.start();}

在该工作线程中:

  • 调用 Looper.prepare()创建与当前线程绑定的Looper实例;
  • 使用上面创建的Looper生成Handler实例;
  • 调用Looper.loop()实现消息循环;

然后透过Looper的循环,在Handler的handlerMessage()中进行异步任务的循环处理。而这也正好是HandlerThread的实现。

@Overridepublic void run() {    mTid = Process.myTid();    Looper.prepare();    synchronized (this) {        mLooper = Looper.myLooper();        notifyAll();    }    Process.setThreadPriority(mPriority);    onLooperPrepared();    Looper.loop();    mTid = -1;}
  • HandlerThread本质上是一个线程类,它继承了Thread;
  • HandlerThread有自己的内部Looper对象,通过Looper.loop()进行looper循环;
  • 通过获取HandlerThread的looper对象传递给Handler对象,然后在handleMessage()方法中执行异步任务;
  • 创建HandlerThread后必须调用HandlerThread.start()方法来启动线程。

二、HandlerThread使用步骤

1、创建HandlerThread实例对象
HandlerThread handlerThread = new HandlerThread("Handler Thread");//HandlerThread handlerThread = new HandlerThread("Handler Thread",Process.THREAD_PRIORITY_DEFAULT);

HandlerThread默认有两个构造函数,提供了线程名参数和线程优先级参数的设置。

2、启动HandlerThread线程
handlerThread.start();

通过start()方法就可以启动一个HandlerThread了,该线程会不断地循环运行。

3、通过Handler构建循环消息处理机制
Handler workderHandler = new Handler(handlerThread.getLooper(), new Handler.Callback() {            @Override            public boolean handleMessage(Message msg) {                //运行在工作线程(子线程)中,用于实现自己的消息处理                return true;            }        });

通过将HandlerThread绑定的Looper对象传递给Handler作为参数,构建一个异步的Handler对象,为了能实现耗时任务的异步执行,我们重写了Handler的Callback接口的handleMessage()方法,当然也可以不重写该方法,而通过post()方法进行耗时任务操作。

Handler workderHandler = new Handler(handlerThread.getLooper());workderHandler.post(new Runnable() {    @Override    public void run() {        //运行在工作线程(子线程)中,用于实现自己的消息处理    }});

最后,我们就可以通过调用workerHandler以发送消息的形式发送耗时任务到工作线程HandlerThread中去执行,实际上就是在Handler.Callback里的handleMessage()中执行。

这里要注意,在创建Handler作为HandlerThread线程消息执行者的时候必须先调用start()方法,因为创建Handler所需要的Looper参数是从HandlerThread中获得的,而Looper对象的赋值又是在HandlerThread的run()方法中创建。

三、HandlerThread使用实例

import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.view.View;import android.widget.Button;import android.widget.TextView;/** * Created by Administrator on 2016/9/18. */public class HandlerThreadActivity extends Activity implements Handler.Callback {    private DBHandlerThread mDBHandlerThread;    private Handler mUIHandler; //与UI线程相关联的Handler    Button mBtnQuery;    TextView mTextResult;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mBtnQuery = (Button) findViewById(R.id.buttonQuery);        mTextResult = (TextView) findViewById(R.id.result);        mBtnQuery.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                //将异步耗时任务发送到HandlerThread中                //Message msg = Message.obtain(null, DBHandlerThread.MSG_QUERY_FRIENDS);                //mDBHandlerThread.getWorkerHandler().sendMessage(msg);                mDBHandlerThread.queryFriends();            }        });        mUIHandler = new Handler(this);        initWorkerThread();    }    protected void initWorkerThread() {        mDBHandlerThread = new DBHandlerThread("Handler Thread");        mDBHandlerThread.setUIHandlerCallBack(mUIHandler);        mDBHandlerThread.start(); //start()后会执行Thread的run()方法    }    @Override    protected void onDestroy() {        super.onDestroy();        mDBHandlerThread.setUIHandlerCallBack(null);        mDBHandlerThread.quit();        mDBHandlerThread = null;    }    @Override    public boolean handleMessage(Message msg) {        switch (msg.what) {            case DBHandlerThread.MSG_QUERY_FRIENDS:                // update UI                break;        }        return false;    }}

DBHandlerThread类如下:

import android.os.Handler;import android.os.HandlerThread;import android.os.Message;/** * Created by Administrator on 2016/9/18. */public class DBHandlerThread extends HandlerThread implements Handler.Callback {    public static final int MSG_QUERY_FRIENDS = 100;    private Handler mWorkerHandler; //与工作线程相关联的Handler    private Handler mUIHandler; //与UI线程相关联的Handler    public DBHandlerThread(String name) {        super(name);    }    public DBHandlerThread(String name, int priority) {        super(name, priority);    }    public void setUIHandlerCallBack(Handler handler) {        this.mUIHandler = handler;    }    public Handler getWorkerHandler() {        return mWorkerHandler;    }    @Override    protected void onLooperPrepared() {        mWorkerHandler = new Handler(getLooper(), this);    }    @Override    public boolean handleMessage(Message msg) {        switch (msg.what) {            case MSG_QUERY_FRIENDS:                //...查询数据库操作...                Message message = Message.obtain(null, MSG_QUERY_FRIENDS);                mUIHandler.sendMessage(message); //通知UI更新                break;        }        return true;    }    public void queryFriends() {        Message msg = Message.obtain(null, MSG_QUERY_FRIENDS);        mWorkerHandler.sendMessage(msg);    }}

四、HandlerThread源码解析

HandlerThread的源码不多,先看下它的构造函数:

/** * Handy class for starting a new thread that has a looper. The looper can then be * used to create handler classes. Note that start() must still be called. */public class HandlerThread extends Thread {    int mPriority; //线程优先级    int mTid = -1; //当前线程id    //当前线程持有的Looper对象    Looper mLooper;    public HandlerThread(String name) {        //调用父类默认的方法创建线程        super(name);        mPriority = Process.THREAD_PRIORITY_DEFAULT;    }   //带优先级参数的构造方法    public HandlerThread(String name, int priority) {        super(name);        mPriority = priority;    }    ...............}

从代码中得知,HandlerThread带有两个构造函数,可传递两个参数,一个参数是name,指的是线程的名称,另一个参数是priority,指的是线程优先级。线程的优先级的取值范围为-20到19。优先级高的获得的CPU资源更多,反之则越少。-20代表优先级最高,19最低。 我们可以根据自己的需要去设置线程的优先级,也可以采用默认的优先级,HandlerThread的默认优先级是Process.THREAD_PRIORITY_DEFAULT,具体值为0。该优先级是再run()方法中设置的,我们看它的run()方法:

public class HandlerThread extends Thread {  /**     * Call back method that can be explicitly overridden if needed to execute some     * setup before Looper loops.     */    protected void onLooperPrepared() {    }    @Override    public void run() {        mTid = Process.myTid(); //获得当前线程的id        Looper.prepare(); //准备循环条件        //通过锁机制来获得当前线程的Looper对象        synchronized (this) {            mLooper = Looper.myLooper();            //唤醒等待线程            notifyAll();        }        //设置当前线程的优先级        Process.setThreadPriority(mPriority);        //在线程循环之前做一些准备工作(子类可实现也可不实现)        onLooperPrepared();        //启动loop        Looper.loop();        mTid = -1;    }}

run()方法主要是通过Looper.prepare()Looper.loop()构造了一个循环线程。这里要注意,在创建HandlerThread对象后必须调用其start()方法才能进行run()方法体的执行。

Looper.prepare()执行后,Looper对象会被创建,然后通过同步锁机制,将Looper对象赋值给HandlerThread的内部变量mLooper,并通过notifyAll()方法去唤醒等待线程。接着为线程赋予优先级,然后执行onLooperPrepared()方法,该方法是一个空实现,留给我们必要的时候去重写的,主要用来做一些初始化工作。最后通过执行Looper.loop()在线程中启动消息队列。

我们看到在run()方法中进行了唤醒等待线程,为什么要这么做呢?答案就在getLooper()方法中:

/** * This method returns the Looper associated with this thread. If this thread not been started * or for any reason is isAlive() returns false, this method will return null. If this thread * has been started, this method will block until the looper has been initialized. * @return The looper. */public Looper getLooper() {   if (!isAlive()) { //判断当前线程是否启动       return null;   }   // If the thread has been started, wait until the looper has been created.   synchronized (this) {       while (isAlive() && mLooper == null) {           try {               wait(); //等待,直到另一个线程调用notify()或notifyAll()来唤醒它           } catch (InterruptedException e) {           }       }   }   return mLooper;}

该方法用来获取当前子线程HandlerThread所关联的Looper对象实例。首先判断HandlerThread线程是否存活,如果没有存活就直接返回null,否则继续执行,进入同步块并判断Looper对象是否为空以及线程是否启动,若都满足,则调用wait()方法进入阻塞阶段,直到Looper对象被成功创建并且通过notifyAll()方法唤醒该等待线程,最后才返回该Looper对象。

Looper对象的创建是在run()方法进行的,也即在子线程中执行的,而getLooper()方法是在UI线程中调用的,若不使用等待唤醒机制,我们就无法保证在UI线程中调用getLooper()方法时Looper对象已经被创建,会面临一个同步的问题,所以HandlerThread就通过等待唤醒机制来解决该同步问题。

HandlerThread既然是一个循环线程,那么怎么退出呢?有两种方式,分别是不安全的退出方法quit()和安全的退出方法quitSafely()

/** * Quits the handler thread's looper. * <p> * Causes the handler thread's looper to terminate without processing any * more messages in the message queue. * </p><p> * Any attempt to post messages to the queue after the looper is asked to quit will fail. * For example, the {@link Handler#sendMessage(Message)} method will return false. * </p><p class="note"> * Using this method may be unsafe because some messages may not be delivered * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure * that all pending work is completed in an orderly manner. * </p> * * @return True if the looper has been asked to quit or false if the * thread had not yet started running. * * @see #quitSafely */public boolean quit() {   Looper looper = getLooper();   if (looper != null) {       looper.quit();       return true;   }   return false;}
/** * Quits the handler thread's looper safely. * <p> * Causes the handler thread's looper to terminate as soon as all remaining messages * in the message queue that are already due to be delivered have been handled. * Pending delayed messages with due times in the future will not be delivered. * </p><p> * Any attempt to post messages to the queue after the looper is asked to quit will fail. * For example, the {@link Handler#sendMessage(Message)} method will return false. * </p><p> * If the thread has not been started or has finished (that is if * {@link #getLooper} returns null), then false is returned. * Otherwise the looper is asked to quit and true is returned. * </p> * * @return True if the looper has been asked to quit or false if the * thread had not yet started running. */public boolean quitSafely() {    Looper looper = getLooper();    if (looper != null) {        looper.quitSafely();        return true;    }    return false;}

通过查看开发者文档知道,quit()方法在API Level 5时被添加,而quitSafely()方法在API Level 18时才被添加。

quit()方法,主要是把MessageQueue中所有的消息全部清空,无论是延迟消息还是非延迟消息。

quitSafely()方法只会清空MessageQueue中所有的延迟消息,并将所有的非延迟消息继续分发出去,最后等到Handler处理完后才停止Looper循环。

这里的延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送的消息。

五、HandlerThread应用场景

HandlerThread比较适用于单线程+异步队列的场景,比如IO读写操作数据库、文件等,耗时不多而且也不会产生较大的阻塞。对于网络IO操作,HandlerThread并不适合,因为它只有一个线程,还得排队一个一个等着。

这里单线程+异步队列模型的使用场景还举几个例子:


    1. 应用刚启动时的数据初始化操作,如果开启多个线程同时执行,有可能争夺UI线程的CPU执行时间,造成卡顿,而使用该模型,通过设置优先级就可以将同步工作顺序的执行,而又不影响UI的初始化;
  • 2.从数据库中读取数据展现在ListView中,通过HandlerpostAtFrontOfQueue方法,快速将读取操作加入队列前端执行,必要时返回给主线程更新UI;

  • 3.HandlerThread应用在IntentService中,IntentService是一个需要长时间执行任务Service,优先级要比线程高。

最后

有需要更多资料的朋友可以私信我【进阶】我可以分享给你。

直接点击即可领取

Android学习PDF+架构视频+面试文档+源码笔记

如果你有其他需要的话,也可以在 GitHub 上查看,下面的资料也会陆续上传到Github

330页PDFAndroid核心笔记