欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android 多线程之HandlerThread介绍

程序员文章站 2022-09-02 19:46:19
一、什么是HandlerThread及特点 HandlerThread本质就是一个Thread,在内部建立了消息队列机制(Looper、MessageQueue),具有消息循环的...

一、什么是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);
                }

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

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循环。