Android 多线程之HandlerThread

一、什么是HandlerThread及特点

HandlerThread本质就是一个Thread,在内部建立了消息队列机制(Looper、MessageQueue),具有消息循环的线程。

特点

  • HandlerThread本质上一个线程类,它继承了Thread
  • HandlerThread与自己的内部Looper对象,可以进行loop循环
  • 通过获取looper对象传递给Handler对象,并可以在Handler的handleMessage方法中执行异步任务
  • 与线程池重并发不同,handlerThread是一个串行队列,背后只有一个线程
  • 优点就是不会堵塞,减少对信性能的消耗,缺点是不能同时进行多任务处理,需要等待处理,效率较低。

二、HandlerThread使用方法

1.创建HandlerThread线程,并在构造方法内传入线程名称,可以是任意字符串

HandlerThread mHandlerThread = new HandlerThread("Handler_Thread");

2.启动线程

 mHandlerThread.start();

3.创建异步Handler对象,并构建Callback消息循环机制

   mchildHandler = new Handler(mHandlerThread.getLooper(), new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {

                //子线程内运行,此处不能更新UI
                Log.d(TAG, "handleMessage: "+msg.what+" 线程名:"+Thread.currentThread().getName());

//                mTvContent.setText(String.valueOf(msg.what));
                //主线程Hanlder,发送消息通知更新UI
                mUIHandler.sendMessage(msg);

                return false;
            }
        });

以上步骤3构建一个完整异步Handler,将HandlerThread的Looper对接和Callback接口类作为参数传递到异步 mchildHandler,这样当前的mchildHandler对象就拥有HandlerThread的Looper对象,由于HandlerThread本身是异步线程,故Looper也绑定到异步线程中,所以handleMessage方法是处理异步耗时任务,当处理状态发生改变,需要UI线程的Handler发送消息通知更新UI。

  mchildHandler = new Handler(mHandlerThread.getLooper(), new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {

                //子线程内运行,此处不能更新UI
                Log.d(TAG, "handleMessage: "+msg.what+" 线程名:"+Thread.currentThread().getName());

//                mTvContent.setText(String.valueOf(msg.what));
                //主线程Hanlder,通知主线程更新
                Message message = Message.obtain();
                message.what=msg.what;
                mUIHandler.sendMessage(message);

                return false;
            }
        });

模拟触发异步线程

for (int i = 0; i <= 10; i++) {
                    Message obtain = Message.obtain();
                    obtain.what = i;
                    mchildHandler.sendMessageAtTime(obtain,1000);
                }

三、HandlerThread源码分析

首先我们从构造函数开始:

public class HandlerThread extends Thread {
    //线程优先级
    int mPriority;
    int mTid = -1;
    //当前线程所持有的Looper对象
    Looper mLooper;
    private @Nullable Handler mHandler;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

    /**
     * Constructs a HandlerThread.
     * @param name
     * @param priority The priority to run the thread at. The value supplied must be from 
     * {@link android.os.Process} and not from java.lang.Thread.
     */
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }

从源码可以看出HandlerThread是继承于Thread,本质上是一个线程,构造方法需要传入两个参数,一个是name指的是线程名称,一个是priority指的是线程的优先级,当创建了HandlerThread对象并开启线程start方法后,run方法就会马上执行,接下来看看run方法:

 /**
     * 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();
        //在当前线程初始化一个Looper对象
        Looper.prepare();
        synchronized (this) {
            //把初始化的Looper对象赋值给HandlerThread的内部mLooper
            mLooper = Looper.myLooper();
            //唤醒等待线程
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

从源码可以看出,当run方法被执行后,会调用Looper.prepare()创建一个Looper对象和MessageQueue对象并绑定了在当前线程(异步线程);这样才可以Looper对象以参数的形式传给Handler对象,从而确保Handler的hanldMessage方法是在子线程(异步线程)中执行的;接下来会把创建的Looper对象赋值给HandlerThread的内部变量mLooper,并通过notifyAll方法唤醒等待线程,最后调用loop方法,中间有个onLooperPrepared方法,可以重写此方法,也就是说在loop开启循环之前,可以做一些其他的操作。有个疑问就是在loop开启循环之前,为什么要唤醒等待线程呢,接下来看看getLooper方法:

  /**
     * This method returns the Looper associated with this thread. If this thread not been started
     * or for any reason 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 .
       返回此线程关联的Looper对象
     */
    public Looper getLooper() {
       //如果线程没有开启,则返回null
         if (!isAlive()) {
            return null;
        }

        // If the thread has been started, wait until the looper has been created.
       //如果线程被开启了,而且当前线程Looper为null时,此时会等待状态,直到mLooper被初始化并赋值后会被唤醒
         synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

当外部主线程调用getLooper方法,先会判断当前异步线程是否启动了,如果isAlive()为false ,说明线程未开启,会返回null,如果线程启动了,则会进入同步代码块内并判断mLooper是否为null,也就是内部变量mLooper是否被赋值,为null表示未赋值尚未初始化,进入等待状态,直到mLooper被赋值后,notifyAll方法会唤醒所以的等待线程 ,最后返回Looper对象。 之所以有个等待唤醒机制,主要getLooper方法是在主线程调用的,而mLooper是在子线程初始化赋值的,也就是我们无法保证不同线程访问同一个对象 是否被创建了,也就是说在调用getLooper方法来获取Looper对象时,无法确保mLooper已经被创建了 ,,故引入等待唤醒机制来确保线程创建开启后 并且Looper对象 也被创建后才可以成功返回mLooper

    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }

    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

线程的退出提供两个方法,一个quit,一个quitSafely,quitSafely相对更安全下。

  • quit:会清除MessageQueue中全部的消息,无论是延迟还是非延迟的消息都清除。

  • quitSafely:该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理完成后才停止Looper循环。

猜你喜欢

转载自blog.csdn.net/hzw2017/article/details/79500054