Android | HandlerThread源码分析

前言

在Android中,默认情况下,一个线程没有与它相关联的Looper。也就是说,默认情况下,我们无法创建与该线程相关联的Handler。HandlerThread是一个继承自Thread的线程类,它用于启动一个有Looper的线程。通过这个Looper,我们就可以创建与该线程相关联的Handler。通过Handler,我们就可以很方便地控制该线程执行我们的耗时任务。在Android系统中,很多地方都使用了HandlerThread。比如,IntentService、WifiScanningService、NetworkStatsService等等。

基础知识

HandlerThread的工作原理基于Android的消息机制。具体Android的消息机制的工作原理可以阅读 Android的消息机制源码分析 这篇文章。

基本用法

HandlerThread的用法非常简单。具体的示例代码如下所示:

HandlerThread thread = new HandlerThread("Thread name");
thread.start();

mLooper = thread.getLooper();
mHandler = new Handler(mLooper) {

    @Override
    public void handleMessage(Message msg) {
        // execute task
    }
};

首先,创建一个HandlerThread线程,并启动该线程。然后,获取该线程的Looper,并使用这个Looper来创建一个与该线程相关联的Handler。最后,通过这个Handler,我们就可以很方便地控制该线程执行我们的耗时任务。我们可以使用发送 Message 的方法,也可以使用投递 Runnable 的方法。

注意:

  • 在获取HandlerThread线程的Looper之前一定要记得先启动线程。
  • 在适当的时候要记得调用mLooper.quit()方法来退出Looper消息循环,结束HandlerThread线程。
  • Looper从MessageQueue中循环地取出Message来处理,所以HandlerThread线程以队列的方式来执行多个任务。

源码分析

我们将按照HandlerThread基本用法的顺序来进行源码分析。

首先,我们要创建一个HandlerThread线程。所以,我们先来看HandlerThread的构造方法。

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

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

HandlerThread提供了两个构造方法。其中,name参数用来设置线程名,priority参数用来设置线程的优先级。第一个构造方法设置HandlerThread线程的优先级为线程的默认优先级。

然后,我们要启动该线程。所以,接下来我们来看HandlerThread的run()方法。

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

在run()方法中,首先调用了Looper.prepare()静态方法来创建一个与该线程相关联的Looper。Looper.prepare()静态方法下面的同步代码块用来同步该线程的Looper对象。接着设置了该线程的优先级。最后调用了Looper.loop()静态方法来启动该线程的消息循环。Looper.loop()静态方法是一个死循环,它循环地取出MessageQueue中的Message来处理。

接着,我们就可以获取该线程的Looper了。所以,接下来我们来看HandlerThread的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;
}

如果在启动线程之前调用了getLooper()方法,那么该方法将返回null。如果在启动线程之后调用了getLooper()方法,那么该方法将阻塞,直到Looper对象创建完毕。所以,我们要先启动HandlerThread线程,然后再通过getLooper()方法来获取与该线程相关联的Looper。获取到Looper之后,我们就可以创建与该线程相关联的Handler了。通过Handler,我们就可以很方便地控制该线程执行我们的耗时任务。

最后,在适当的时候我们要调用mLooper.quit()方法来退出Looper消息循环,结束HandlerThread线程。所以,最后我们来看HandlerThread的quit()方法。

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

quit()方法调用了Looper的quit()方法来退出Looper.loop()死循环,结束HandlerThread线程。

总结

HandlerThread的工作原理基于Android的消息机制。通过使用HandlerThread线程,我们可以很方便地控制该线程执行我们的耗时任务。

例子

这里举一个简单的例子来实践HandlerThread。项目源码地址:https://github.com/chongyucaiyan/HandlerDemo

我们创建了一个HandlerThread线程,并创建了一个与它相关联的Handler。通过这个Handler,我们发送Message来控制HandlerThread线程执行耗时任务。具体的代码和注释如下所示:

public class MainActivity extends AppCompatActivity {
    private static final int DO_TASK1 = 1;
    private static final int DO_TASK2 = 2;

    private Looper mLooper;
    private MyHandler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 创建HandlerThread线程
        createHandlerThread();
    }

    private void createHandlerThread() {
        // 创建HandlerThread线程,并启动该线程
        HandlerThread thread = new HandlerThread("HandlerThread");
        thread.start();
        // 获取线程的Looper,创建与该线程相关联的Handler
        mLooper = thread.getLooper();
        mHandler = new MyHandler(mLooper);
        // 发送任务消息
        sendMessage();
    }

    private void sendMessage() {
        Message message1 = mHandler.obtainMessage(DO_TASK1);
        Message message2 = mHandler.obtainMessage(DO_TASK2);
        mHandler.sendMessage(message1);
        mHandler.sendMessage(message2);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 退出Looper消息循环
        mLooper.quit();
    }

    private static class MyHandler extends Handler {
        private static final String TAG = "MyHandler";

        public MyHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case DO_TASK1:
                    // 执行任务1
                    doTask1();
                    break;
                case DO_TASK2:
                    // 执行任务2
                    doTask2();
                    break;
                default:
                    break;
            }
        }

        private void doTask1() {
            Log.i(TAG, "doTask1(), start");
            Log.i(TAG, "doTask1(), thread name is " + Thread.currentThread().getName());

            // 模拟耗时任务
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            Log.i(TAG, "doTask1(), end");
        }

        private void doTask2() {
            Log.i(TAG, "doTask2(), start");
            Log.i(TAG, "doTask2(), thread name is " + Thread.currentThread().getName());

            // 模拟耗时任务
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            Log.i(TAG, "doTask2(), end");
        }
    }
}

运行应用,打印的信息如下图所示:

任务执行打印的信息.png

可以看到:

  1. 任务是在HandlerThread子线程中执行的。
  2. 任务2是在任务1执行完毕之后才开始执行的。这验证了HandlerThread线程是以队列的方式来执行多个任务的。

参考

  • Android 7.1.1 (API level 25)
  • https://developer.android.com/reference/android/os/HandlerThread.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值