详解 Android 中的 HandlerThread

HandlerThread 是 Android API 提供的一个便捷的类,使用它我们可以快速的创建一个带有 Looper 的线程,有了 Looper 这个线程,我们又可以生成 Handler,那么 HandlerThread 是什么,可以做什么呢,有哪些奇技淫巧可以被我们利用呢?

实现原理

在介绍原理之前,我们先使用普通的 Thread 来创建一个 Handler,创建的过程大致如下:

Handler mHandler;
private void createManualThreadWithHandler() {
  new Thread() {
      @Override
        public void run() {
            super.run();
            Looper.prepare();
            mHandler = new Handler(Looper.myLooper());
            Looper.loop();
        }
    }.start();
}

实现很简单,在目标线程内如下配置

  • 调用 Looper.prepare 创建与当前线程绑定的 Looper 实例
  • 使用上面创建的 Looper 生成 Handler 实例
  • 调用 Looper.loop()实现消息循环 HandlerThread 是 Android API 提供的一个便捷的类,使用它我们可以快速的创建一个带有 Looper 的线程,有了 Looper 这个线程,我们又可以生成 Handler,那么 HandlerThread 是什么,可以做什么呢,有哪些奇技淫巧可以被我们利用呢?

实现原理

在介绍原理之前,我们先使用普通的 Thread 来创建一个 Handler,创建的过程大致如下:

Handler mHandler;
private void createManualThreadWithHandler() {
new Thread() {
@Override
public void run() {
super.run();
Looper.prepare();
mHandler = new Handler(Looper.myLooper());
Looper.loop();
}
}.start();
}

实现很简单,在目标线程内如下配置

  • 调用 Looper.prepare 创建与当前线程绑定的 Looper 实例
  • 使用上面创建的 Looper 生成 Handler 实例
  • 调用 Looper.loop()实现消息循环

明白上面的实现步骤,HandlerThread 的实现也就简单了,其实现为:

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

确实很简单,无需赘述。

Handler 原理

要理解 Handler 的原理,理解如下几个概念即可茅塞顿开。

  • Message 意为消息,发送到 Handler 进行处理的对象,携带描述信息和任意数据。
  • MessageQueue 意为消息队列,Message 的集合。
  • Looper 有着一个很难听的中文名字,消息泵,用来从 MessageQueue 中抽取 Message,发送给 Handler 进行处理。
  • Handler 处理 Looper 抽取出来的 Message。

如何使用

HandlerThread 使用起来很容易,首先需要进行初始化。

private Handler mHandler;
private LightTaskManager() {
HandlerThread workerThread = new HandlerThread("LightTaskThread");
workerThread.start();
mHandler = new Handler(workerThread.getLooper());
}

注意:上面的workerThread.start();必须要执行。 至于如何使用 HandlerThread 来执行任务,主要是调用 Handler 的 API

  • 使用 post 方法提交任务,postAtFrontOfQueue 将任务加入到队列前端,postAtTime 指定时间提交任务,postDelayed 延后提交任务。
  • 使用 sendMessage 方法可以发送消息,sendMessageAtFrontOfQueue 将该消息放入消息队列前端,sendMessageAtTime 指定时间发送消息,sendMessageDelayed 延后提交消息。

通过包裹 Handler API,我们可以实现如下代码(仅 post 相关方法):

public void post(Runnable run) {
mHandler.post(run);
}

public void postAtFrontOfQueue(Runnable runnable) {
mHandler.postAtFrontOfQueue(runnable);
}

public void postDelayed(Runnable runnable, long delay) {
mHandler.postDelayed(runnable, delay);
}

public void postAtTime(Runnable runnable, long time) {
mHandler.postAtTime(runnable, time);
}

控制优先级

了解到如何使用之外,关于 HandlerThread 的使用需要上升一个界别,那就是优化。这里的优化主要是合理调整 HandlerThread 的优先级。 HandlerThread 的默认优先级是Process.THREAD_PRIORITY_DEFAULT,具体值为 0。线程的优先级的取值范围为-20 到 19。优先级高的获得的 CPU 资源更多,反之则越少。-20 代表优先级最高,19 最低。0 位于中间位置,但是作为工作线程的 HandlerThread 没有必要设置这么高的优先级,因而需要我们降低其优先级。

可控制的优先级

  • THREAD_PRIORITY_DEFAULT,默认的线程优先级,值为 0。
  • THREAD_PRIORITY_LOWEST,最低的线程级别,值为 19。
  • THREAD_PRIORITY_BACKGROUND 后台线程建议设置这个优先级,值为 10。
  • THREAD_PRIORITY_MORE_FAVORABLE 相对 THREAD_PRIORITY_DEFAULT 稍微优先,值为-1。
  • THREAD_PRIORITY_LESS_FAVORABLE 相对 THREAD_PRIORITY_DEFAULT 稍微落后一些,值为 1。

以上的这些优先级都是可以在程序中设置的,除此之外还有不可控的优先级均有系统进行自动调整。

如何修改权限

最通用的就是在 run 方法中,加入合理的设置优先级代码,比如

Runnable run = new Runnable() {
@Override
public void run() {
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
}
};
LightTaskManager.getInstance().post(run);

上述方法不仅适用于 HandlerThread,也可以适用于其他的线程。 除此之外,HandlerThread 的构造方法也提供了设置优先级的功能。用法如下:

HandlerThread workerThread = new HandlerThread("LightTaskThread", Process.THREAD_PRIORITY_BACKGROUND);

关于设置优先级,系统的 AsyncTask 已经开始进行了默认设置,将线程的优先级设置成 THREAD_PRIORITY_BACKGROUND 了。

public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);

            Process.setThreadPriority(Process.THREAD\_PRIORITY\_BACKGROUND);
            //noinspection unchecked
            Result result = doInBackground(mParams);
            Binder.flushPendingCommands();
            return postResult(result);
        }
    };

}

关于 Android 中线程的调度详情,请参考剖析 Android 中进程与线程调度之 nice

应用场景

我们可以使用 HandlerThread 处理本地 IO 读写操作(数据库,文件),因为本地 IO 操作大多数的耗时属于毫秒级别,对于单线程 + 异步队列的形式 不会产生较大的阻塞。因此在这个 HandlerThread 中不适合加入网络 IO 操作。 对于本地 IO 读取操作,我们可以使用 postAtFrontOfQueue 方法,快速将读取操作加入队列前端执行,必要时返回给主线程更新 UI。示例场景,从数据库中读取数据展现在 ListView 中。注意读取也是需要花费一定时间,推荐在数据展示之前有必要的用户可感知进度提示。 对于本地 IO 写操作,根据具体情况,选择 post 或者 postDelayed 方法执行。比如 SharedPreference commit,或者文件写入操作。

此文章转载于:https://droidyue.com/blog/2015/11/08/make-use-of-handlerthread/
明白上面的实现步骤,HandlerThread 的实现也就简单了,其实现为:

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

 

确实很简单,无需赘述。

Handler 原理

要理解 Handler 的原理,理解如下几个概念即可茅塞顿开。

  • Message 意为消息,发送到 Handler 进行处理的对象,携带描述信息和任意数据。
  • MessageQueue 意为消息队列,Message 的集合。
  • Looper 有着一个很难听的中文名字,消息泵,用来从 MessageQueue 中抽取 Message,发送给 Handler 进行处理。
  • Handler 处理 Looper 抽取出来的 Message。
     

如何使用

HandlerThread 使用起来很容易,首先需要进行初始化。

private Handler mHandler;
private LightTaskManager() {
    HandlerThread workerThread = new HandlerThread("LightTaskThread");
    workerThread.start();
    mHandler = new Handler(workerThread.getLooper());
}

 

注意:上面的workerThread.start();必须要执行。

至于如何使用 HandlerThread 来执行任务,主要是调用 Handler 的 API

  • 使用 post 方法提交任务,postAtFrontOfQueue 将任务加入到队列前端,postAtTime 指定时间提交任务,postDelayed 延后提交任务。
  • 使用 sendMessage 方法可以发送消息,sendMessageAtFrontOfQueue 将该消息放入消息队列前端,sendMessageAtTime 指定时间发送消息,sendMessageDelayed 延后提交消息。
    通过包裹 Handler API,我们可以实现如下代码(仅 post 相关方法):
public void post(Runnable run) {
    mHandler.post(run);
}

public void postAtFrontOfQueue(Runnable runnable) {
mHandler.postAtFrontOfQueue(runnable);
}

public void postDelayed(Runnable runnable, long delay) {
mHandler.postDelayed(runnable, delay);
}

public void postAtTime(Runnable runnable, long time) {
mHandler.postAtTime(runnable, time);
}

控制优先级

了解到如何使用之外,关于 HandlerThread 的使用需要上升一个界别,那就是优化。这里的优化主要是合理调整 HandlerThread 的优先级。

HandlerThread 的默认优先级是Process.THREAD_PRIORITY_DEFAULT,具体值为 0。线程的优先级的取值范围为-20 到 19。优先级高的获得的 CPU 资源更多,反之则越少。-20 代表优先级最高,19 最低。0 位于中间位置,但是作为工作线程的 HandlerThread 没有必要设置这么高的优先级,因而需要我们降低其优先级。

可控制的优先级

  • THREAD_PRIORITY_DEFAULT,默认的线程优先级,值为 0。
  • THREAD_PRIORITY_LOWEST,最低的线程级别,值为 19。
  • THREAD_PRIORITY_BACKGROUND 后台线程建议设置这个优先级,值为 10。
  • THREAD_PRIORITY_MORE_FAVORABLE 相对 THREAD_PRIORITY_DEFAULT 稍微优先,值为-1。
  • THREAD_PRIORITY_LESS_FAVORABLE 相对 THREAD_PRIORITY_DEFAULT 稍微落后一些,值为 1。
    以上的这些优先级都是可以在程序中设置的,除此之外还有不可控的优先级均有系统进行自动调整。

如何修改权限

最通用的就是在 run 方法中,加入合理的设置优先级代码,比如

Runnable run = new Runnable() {
    @Override
    public void run() {
        android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    }
};
LightTaskManager.getInstance().post(run);

上述方法不仅适用于 HandlerThread,也可以适用于其他的线程。

除此之外,HandlerThread 的构造方法也提供了设置优先级的功能。用法如下:

HandlerThread workerThread = new HandlerThread("LightTaskThread", Process.THREAD_PRIORITY_BACKGROUND);

关于设置优先级,系统的 AsyncTask 已经开始进行了默认设置,将线程的优先级设置成 THREAD_PRIORITY_BACKGROUND 了。

public AsyncTask() {
    mWorker = new WorkerRunnable&lt;Params, Result&gt;() {
        public Result call() throws Exception {
            mTaskInvoked.set(true);

            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            //noinspection unchecked
            Result result = doInBackground(mParams);
            Binder.flushPendingCommands();
            return postResult(result);
        }
    };
}

关于 Android 中线程的调度详情,请参考剖析 Android 中进程与线程调度之 nice

应用场景

我们可以使用 HandlerThread 处理本地 IO 读写操作(数据库,文件),因为本地 IO 操作大多数的耗时属于毫秒级别,对于单线程 + 异步队列的形式 不会产生较大的阻塞。因此在这个 HandlerThread 中不适合加入网络 IO 操作。

对于本地 IO 读取操作,我们可以使用 postAtFrontOfQueue 方法,快速将读取操作加入队列前端执行,必要时返回给主线程更新 UI。示例场景,从数据库中读取数据展现在 ListView 中。注意读取也是需要花费一定时间,推荐在数据展示之前有必要的用户可感知进度提示。

对于本地 IO 写操作,根据具体情况,选择 post 或者 postDelayed 方法执行。比如 SharedPreference commit,或者文件写入操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值