深入浅出Handler(五)HandlerThread

309 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情

一、前言

在Handler系列的第一篇我们简单的用了一个Demo展示了在子线程中定义Handler,我们需要初始化Looper,还需要调用looper.prepare和looper.loop,才能创建一个循环消息机制

其实Android内部已经有一个帮我们封装好的HandlerThread了,今天咱们就来探究一下HandlerThread的源码及使用情况

二、HandlerThread

2.1 成员变量

public class HandlerThread extends Thread {
    //线程任务优先级
    int mPriority;
    int mTid = -1;
    //当前线程的Looper对象
    Looper mLooper;
    //传递消息的handler
    private @Nullable Handler mHandler;
    ...
  • 可以看到HandlerThread继承自Thread类,自己是有run方法可以执行耗时任务的.
  • 同时在成员变量中我们看到了Looper和Handler,都已经帮我们准备好了

2.2 构造函数

//线程名字,方便后续查找,优先级默认
public HandlerThread(String name) {
    super(name);
    mPriority = Process.THREAD_PRIORITY_DEFAULT;
}

//自定义线程名字+优先级
public HandlerThread(String name, int priority) {
    super(name);
    mPriority = priority;
}

2.3 run()方法

@Override
public void run() {
    mTid = Process.myTid();
    //ThreadLocal,创建子线程的looper
    Looper.prepare();
    synchronized (this) {
        //获取当前线程的looper
        mLooper = Looper.myLooper();
        //唤醒所有等待--下面分析
        notifyAll();
    }
    //设置线程优先级
    Process.setThreadPriority(mPriority);
    //looper准备工作
    onLooperPrepared();
    //looper开始轮询
    Looper.loop();
    mTid = -1;
}

2.4 使用方法

HandlerThread handlerThread = new HandlerThread("handler-Thread");
handlerThread.start();
Handler childHandler = new Handler(handlerThread.getLooper()){
    @Override
    public void handleMessage(@NonNull Message msg) {
        super.handleMessage(msg);
        //处理其他线程发送的消息
        //运行在子线程
    }
};

因为我们的子线程处理消息的handler此时是在子线程,而此时handler实例化时是需要传入当前线程的looper的,所以对应的实例化是需要拿到thread的looper

2.5 getLooper()方法

public Looper getLooper() {
    //判断是否存活
    if (!isAlive()) {
        return null;
    }

    boolean wasInterrupted = false;

    //如果已经存活,等待looper在run()方法中创建
    synchronized (this) {
        while (isAlive() && mLooper == null) {
            //死循环,等待
            try {
                //唤醒-对应run()方法里的notifyAll()
                wait();
            } catch (InterruptedException e) {
                wasInterrupted = true;
            }
        }
    }

    
    if (wasInterrupted) {
        Thread.currentThread().interrupt();
    }
    
    //返回looper对象

    return mLooper;
}

可以看到这里返回Looper对象,首先判断线程是否存活,然后判断looper是否已经创建成功,如果looper是null,就wait等待唤醒notify,等run()方法执行完毕并创建成功.

此方法确保了在使用Handler之前已经创建了在子线程中的Looper

2.6 quit()

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;
}

可以看到HandlerThread提供了两个退出的方法,一个是直接退出,一个是安全退出.而它们的调用都是依赖looper.对应的是MessageQueue

void quit(boolean safe) {
    if (!mQuitAllowed) {
        throw new IllegalStateException("Main thread not allowed to quit.");
    }

    synchronized (this) {
        if (mQuitting) {
            return;
        }
        mQuitting = true;

        if (safe) {
            removeAllFutureMessagesLocked();
        } else {
            removeAllMessagesLocked();
        }

        // We can assume mPtr != 0 because mQuitting was previously false.
        nativeWake(mPtr);
    }
}
  • 当我们调用了quit方法时,其实最终是调用了MessageQueueremoveAllFutureMessagesLocked方法,该方法是清空MessageQueue消息池中的所有消息,包括延迟消息和非延迟消息
  • 当我们调用了quitSafely方法时,其实是调用了removeAllMessagesLocked方法,该方法会清空MessageQueue消息池中的全部延迟消息,同时将消息池中全部的非延迟消息派发出去让Handler处理

quitSafely相比较quit方法,会派发全部的非延迟消息

三、Demo使用

接下来我们简单的用一个Demo演示一下如何使用HandlerThread

public class HandlerThreadActivity extends AppCompatActivity {


    private ActivityHandlerThreadBinding binding;
    private MainHandler mainHandler;

    private static class MainHandler extends Handler {
        private WeakReference<HandlerThreadActivity> handlerThreadActivity;

        public MainHandler(HandlerThreadActivity handlerThreadActivity) {
            this.handlerThreadActivity = new WeakReference<>(handlerThreadActivity);
        }

        @Override
        public void handleMessage(@NonNull Message msg) {
            //主线程的handler
            super.handleMessage(msg);
            String description = msg.getData().getString("data");
            Log.i("data", "主线程" + description);
            if (handlerThreadActivity.get() != null) {
                handlerThreadActivity.get().binding.tvShow.setText(description);
            }
        }
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityHandlerThreadBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        mainHandler = new MainHandler(HandlerThreadActivity.this);

        HandlerThread handlerThread = new HandlerThread("handler-Thread");
        handlerThread.start();
        //子线程接收多个msg
        Handler childHandler = new Handler(handlerThread.getLooper()) {
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                //处理其他线程发送的消息
                //运行在子线程
                String description = msg.getData().getString("data");
                Log.i("data", "子线程" + description);
                Message message = mainHandler.obtainMessage();
                Bundle bundle = message.getData();
                bundle.putString("data", description);
                //调用主线程的handler发送消息
                mainHandler.sendMessage(message);

            }
        };

        for (int i = 0; i < 10; i++) {
            //子线程发送消息
            Message message = childHandler.obtainMessage();
            Bundle data = new Bundle();
            data.putString("data", "hello world" + i);
            message.setData(data);
            childHandler.sendMessageDelayed(message, 1000 * i);
        }

    }
}

3.1 系统使用

而我们也在Android源码里看到很多耗时任务的类中都定义使用了HandlerThread

image.png


NotificationManagerCompat.java
SideChannelManager(Context context) {
    mContext = context;
    mHandlerThread = new HandlerThread("NotificationManagerCompat");
    mHandlerThread.start();
    mHandler = new Handler(mHandlerThread.getLooper(), this);
}

3.2小结

可以看到,使用的具体步骤是

  • 创建HandlerThread
  • 通过start启动
  • HandlerThread在run方法内部创建了Looper对象
  • handler对象创建并且和looper绑定
  • 在子线程的handler的handleMessage方法内,处理消息

同时需要注意的是,handlerThread内部的run()方法的looper()循环也是是一个无限循环,当我们的任务已经执行完毕后,需要及时调用quit()或者quitSafely()方法退出循环

四、总结

当我们在开发中需要频繁的开启子线程做耗时操作,通过创建多个匿名线程

new Thread(){}.start();

会使得程序变慢,当我们有耗时任务需要在子线程执行,并且需要与主线程进行一些交互时可以使用HandlerThread,注意执行完耗时任务后记得关闭looper的循环.

五、彩蛋

在上面使用handler发送消息时,不知道你是否注意到,我们在新建一个消息时没有简单的new一个消息,而是使用了

Message message = handler.obtainMessage();

对于需要处理大量消息的MessageQueue来说,它的消息池肯定是存在大量新建的消息以及被处理过的消息的,那么它是如何存储这些消息,保证消息体的复用效率的呢?

这也是下一章我们需要讨论的问题~