浅谈HandlerThread

前言:我们知道在Android系统中,我们执行完耗时操作都要另外开启子线程来执行,执行完线程以后线程会自动销毁。想象一下如果我们在项目中经常要执行耗时操作,如果经常要开启线程,接着又销毁线程,这无疑是很消耗性能的?那有什么解决方法呢?

一般有两种:

  • 使用线程池
  • 使用HandlerThread

  而HandlerThread是android系统帮我们封装的一个异步处理任务的Thread,并且内部封装了looper对象。

HandlerThread特点:

  • HandlerThread本质上是一个线程类,它继承了Thread;
  • HandlerThread有自己的内部Looper对象,可以进行looper循环;
  • 通过获取HandlerThread的looper对象传递给Handler对象,可以在handleMessage方法中执行异步任务。
  • 创建HandlerThread后必须先调用HandlerThread.start()方法,Thread会先调用run方法,创建Looper对象。

一、HandlerThread常规使用步骤


1.创建实例对象

HandlerThread handlerThread = new HandlerThread("downloadImage");

2.启动HandlerThread线程

handlerThread.start();

3.构建循环消息处理机制

  /**
     * 该callback运行于子线程
     */
class ChildCallback implements Handler.Callback {
        @Override
        public boolean handleMessage(Message msg) {
            //在子线程中进行相应的网络请求

            //通知主线程去更新UI
            mUIHandler.sendMessage(msg1);
            return false;
        }
    }

4.构建异步handler

Handler childHandler = new Handler(handlerThread.getLooper(),new ChildCallback());


二、HandlerThread源码解析


  先贴出完整的源码,HandlerThread的源码非常精简,只有不到100行:

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;

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

    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }

    /**
     * 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.prepare();
        //持有锁机制来获得当前线程的Looper对象
        synchronized (this) {
            mLooper = Looper.myLooper();
            //发出通知,当前线程已经创建mLooper对象成功,这里主要是通知getLooper方法中的wait
            notifyAll();
        }
        //设置线程的优先级别
        Process.setThreadPriority(mPriority);
        //这里默认是空方法的实现,我们可以重写这个方法来做一些线程开始之前的准备,方便扩展
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        // 直到线程创建完Looper之后才能获得Looper对象,Looper未创建成功,阻塞
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return 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;
    }

    /**
     * Returns the identifier of this thread. See Process.myTid().
     */
    public int getThreadId() {
        return mTid;
    }
}

1.先分析构造方法

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

    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }

  可以看出HandlerThread继续自Thread,有两个构造方法,构造方法的传递参数分别有一个和有两个,一个参数的构造方法的参数是name指的是线程的名称;两个参数的构造方法的参数一个是name指的是线程的名称,一个是priority指的是线程优先级;我们根据需要调用即可。

    int mPriority;//线程优先级
    int mTid = -1;
    Looper mLooper;//当前线程持有的Looper对象

    protected void onLooperPrepared() {
        } 

  其中成员变量mLooper就是HandlerThread自己持有的Looper对象。onLooperPrepared()该方法是一个空实现,是留给我们必要时可以去重写的,但是注意重写时机是在Looper循环启动前。

2.再分析run方法

 @Override
public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll(); //唤醒等待线程
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
   }

  在创建HandlerThread对象后必须调用其start()方法才能进行其他操作,而调用start()方法后相当于启动了线程,也就是run方法将会被调用。因为HandlerThread是继承与Thread类的。

  从代码我们可以看到Looper.prepare(),这里Looper对象被创建,这样我们再构建异步handler时才可以把Looper对象给Handler对象,进而确保Handler对象中的handleMessage方法是在异步线程执行的。

接着执行

synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll(); //唤醒等待线程
        }

这里唤醒了等待线程,原因就要看看getLooper()代码了:

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();//等待唤醒
           } catch (InterruptedException e) {
           }
       }
   }
   return mLooper;
}

  先用isAlive()判断线程是否启动,不然线程没启动就不存在Looper,自然返回空(null),如果判断true进行下个判断,Looper的创建是在子线程中执行的,而调用getLooper方法则是在主线程进行的,无法保证在调用getLooper方法时Looper已经被创建,所以要判断mLooper == null,是空的话就让getLooper方法先等待,让run方法创建完成之后notifyAll()来唤醒线程,这就是notifyAll()的原因了。

3.最后看看quit和quitSafely方法

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方法实际是调用Looper的quit方法,该方法主要是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送)还是非延迟消息。

  quitSafely方法实际是调用Looper的quitSafely方法,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理完成后才停止Looper循环,quitSafely相比于quit方法安全的原因在于清空消息之前会派发所有的非延迟消息。

三、总结


  HandlerThread对管理线程的开启和销毁做出了自己的封装,并且可以通过getLooper方法来传给Handler,可以在handleMessage方法中执行异步任务。避免了频繁开启多个线程和handler组合消耗大量性能的问题。

猜你喜欢

转载自blog.csdn.net/weixin_38364803/article/details/80368259