HandlerThread源码分析

HandlerThread源码分析

概述

   在之前的文章中介绍过Android应用层的消息机制【点这里 】,也介绍了利用Handler机制实现多线程调度的AsyncTask,接下来介绍一个新成员HandlerThread。从字面上可以看出,HandlerThread可能是与Thread与Handler应用相关的,HandlerThread也确实是封装了Handler功能的Thread,接下来我们从其基本使用,主要用途,源码等方面来介绍HandlerThread。

基本使用

   一般使用HandlerThread步骤如下:

@Override
public void onCreate() {
	// 1.创建线程
	HandlerThread thread = new HandlerThread("myHandlerThread");
	// 2.启动线程
	thread.start();
}

// 3.创建与当前线程关联的handler,这里使用了HandlerThread创建的looper
Handler handler = new Handler(thread.getLooper()) {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        // 消息处理,非主线程消息
    }
});

@Override
public void onStart(@Nullable Intent obj, int arg) {
	// 4.利用Handler向绑定的线程发送消息
	Message msg = handler.obtainMessage();
	msg.arg1 = arg;
	msg.obj = obj;
	handler.sendMessage(msg);
}

@Override
public void onDestroy() {
	// 5.调用生命周期结束,停止消息队列循环
	thread.quitSafely();
}

   对HandlerThread使用步骤大概如上述5点,使用比较简单,后续的源码分析时,会简单介绍为什么是这些步骤。

主要用途

   HandlerThread的主要用途与注意点如下:

  • HandlerThread将loop转到子线程中处理,主要目的是分担MainLooper的工作量,降低了主线程的压力,使主界面更流畅。
  • 一个线程起到多个线程工作的特点,处理任务是串行执行,按消息发送顺序进行处理。HandlerThread本质是一个线程,在线程内部,代码是串行处理的。
  • HandlerThread不适合处理类似网络I/O操作这样的并发任务,而且效率可能比较低,适合轻量级任务使用。

   HandlerThread最典型的应用就是IntentService,我们都知道IntentService是特殊的Service,其内部就是使用HandlerThread来实现子线程处理任务的。

源码分析

   为了更好的理解HandlerThread的使用,下面我们来了解下源码。

   在文章开头我们介绍了HandlerThread的基本使用步骤,我们就按步骤的实现一一来看下源码,第一步我们创建了对象,所以先看下构造函数。

public class HandlerThread extends Thread {
    Looper mLooper;
    
    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,因此它具备线程的基本使用功能,其构造方法有两个,一个是单参数方法可设置线程名,一个是双参数方法可设置线程名与线程优先级,构造方法其实没做什么工作。接下来看第二步,调用了start()方法启动了线程,所以我们看看run()方法里做了什么。

    @Override
    public void run() {
        // 线程标识
        mTid = Process.myTid();
        // 准备looper
        Looper.prepare();
        // 获取looper对象,注意这里的同步块
        synchronized (this) {
            // 获取looper
            mLooper = Looper.myLooper();
            notifyAll();
        }
        // 设置线程优先级
        Process.setThreadPriority(mPriority);
        // 钩子函数
        onLooperPrepared();
        // 消息队列启动循环
        Looper.loop();
        // 为了更好的回收loop,标识重置
        mTid = -1;
    }

   我们都知道,要在子线程中使用Looper必须做上述的调度,HandlerThread帮我们已经封装好了。值得注意的是代码中的同步块,它的作用其实是为了后面获取使用Looper准备的,所以我们继续看第三步Handler创建时getLooper()方法。

    public Looper getLooper() {
        // 1.如果线程没start则直接返回空
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        // 2.如果线程start了则等待looper创建好
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

   结合第二步骤源码中的同步块,就可以看到,当尝试使用HandlerThread中的Looper创建Handler时,HandlerThread内部会利用信号量与同步机制来保证消息队列Looper的创建与运行,只有线程正常运行了,才能正常获取到Looper对象。

   第四步则是利用消息机制实现任务调度,这里不再赘述。

   第五步主要目的是退出当前线程的Looper,因为没有用的情况下,开着Looper会消耗资源。查看HandlerThread源码,其实提供了两个停止Looper的方法,如下:

    /**
     * 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 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 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()方法调度是不安全的,他不会处理消息队列中剩余的消息就停止了队列循环;quitSafely()方法则是会处理调停止前还在队列中的消息,然后停止循环。具体调哪个方法,就要跟进使用场景来看了,读者可以自己权衡。

   就此对于HandlerThread源码分析就此结束,由于笔者技术有限,如果分析过程中有出现错误,欢迎指出,谢谢。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值