HandlerThread是什么?
HandlerThread是android SDK中android.os包下的类,继承于Thread类,所以是一个线程类,它存在的价值是什么呢?答案就是,HandlerThread可以创建一个自带Looper的线程,自动处理Looper创建时的同步问题。
HandlerThread源码简析
public class HandlerThread extends Thread {
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
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;
}
}
关键点就在这两个方法中的synchronized同步块,wait和notifyAll协作使用,保证了mLooper创建的同步。例如,在UI线程中创建了HandlerThread线程对象,并通过getLooper方法获取该线程的Looper,如果mLooper为null,并且线程alive的话,会一直等待直到mLooper实例化结束,自动完成线程同步。
Launcher中应用场景分析
下面的几个场景的源码,均来自Android P中的launcher3,大家可以去查阅对应代码。
1、LauncherModel中场景分析
先来看下核心的几行代码:
public class LauncherModel extends BroadcastReceiver
implements LauncherAppsCompat.OnAppsChangedCallbackCompat {
@Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
static {
sWorkerThread.start();
}
@Thunk static final Handler sWorker = new Handler(sWorkerThread.getLooper());
private static void runOnWorkerThread(Runnable r) {
if (sWorkerThread.getThreadId() == Process.myTid()) {
r.run();
} else {
// If we are not on the worker thread, then post to the worker handler
sWorker.post(r);
}
}
}
这里有一个需要注意的地方,这里是在静态块中启动的sWorkerThread,sWorker是以静态变量的形式实例化的,必须要保证先后顺序,因为静态变量和静态代码块的加载顺序由编写先后决定,顺序颠倒,sWorkerThread.getLooper()会返回null。
LauncherModel中几乎所有耗时任务都是通过sWorkerThread完成的,如果存在多个pending中的任务,就按先后顺序排队,有点类似单线程池的作用。
2、WallpaperManagerCompatVL中场景分析
下面是JobService中的一个应用场景,代码如下:
public static class ColorExtractionService extends JobService implements Runnable {
private HandlerThread mWorkerThread;
private Handler mWorkerHandler;
@Override
public void onCreate() {
super.onCreate();
mWorkerThread = new HandlerThread("ColorExtractionService");
mWorkerThread.start();
mWorkerHandler = new Handler(mWorkerThread.getLooper());
}
@Override
public void onDestroy() {
super.onDestroy();
mWorkerThread.quit();
}
@Override
public boolean onStartJob(final JobParameters jobParameters) {
mWorkerHandler.post(this);
return true;
}
@Override
public boolean onStopJob(JobParameters jobParameters) {
mWorkerHandler.removeCallbacksAndMessages(null);
return true;
}
@Override
public void run() {
}
}
JobService在线程中处理颜色提取任务,这里HandlerThread使用挺典型的,可以作为自己实现的参考。HandlerThread启动后,因为存在loop循环,是有一定性能损耗的,这里在onDestroy时调用mWorkerThread.quit()来停止loop,是个不错的实践。
3、FileLog中场景分析
Launcher3中实现了一个往文件中写log的工具类,后台线程也是HandlerThread实现的。主要代码如下:
public final class FileLog {
private static Handler getHandler() {
synchronized (DATE_FORMAT) {
if (sHandler == null) {
HandlerThread thread = new HandlerThread("file-logger");
thread.start();
sHandler = new Handler(thread.getLooper(), new LogWriterCallback());
}
}
return sHandler;
}
public static void print(String tag, String msg, Exception e) {
......
Message.obtain(getHandler(), LogWriterCallback.MSG_WRITE, out).sendToTarget();
}
}
这里使用HandlerThread也挺巧妙的,HandlerThread可以循环处理消息,不用每写一个log就创建一个线程去处理,还有就是,HandlerThread可以保证顺序执行,按先后去写日志。
4、UiThreadHelper中场景分析
UiThreadHelper是UI线程的辅助类,从Process.THREAD_PRIORITY_FOREGROUND线程优先级可以看出,这里的HandlerThread是一个UI前台线程,是为了分流UI主线程的工作,把一部分界面交互处理放到子线程去做。
/**
* Utility class for offloading some class from UI thread
*/
public class UiThreadHelper {
private static HandlerThread sHandlerThread;
private static Handler sHandler;
private static final int MSG_HIDE_KEYBOARD = 1;
public static Looper getBackgroundLooper() {
if (sHandlerThread == null) {
sHandlerThread =
new HandlerThread("UiThreadHelper", Process.THREAD_PRIORITY_FOREGROUND);
sHandlerThread.start();
}
return sHandlerThread.getLooper();
}
private static Handler getHandler(Context context) {
if (sHandler == null) {
sHandler = new Handler(getBackgroundLooper(),
new UiCallbacks(context.getApplicationContext()));
}
return sHandler;
}
public static void hideKeyboardAsync(Context context, IBinder token) {
Message.obtain(getHandler(context), MSG_HIDE_KEYBOARD, token).sendToTarget();
}
}
使用注意
调用HandlerThreadd的getLooper()方法获取Looper对象之前,需要先启动HandlerThread,不然线程处于非启动状态,一直返回空Looper对象。
结语
个人理解,HandlerThread首先是个线程,继承了线程的一般特性,不过,它有自己的特殊能力,可以无限循环去处理队列中的任务,有点类似单线程池的作用,但是它也有自己的杀手锏,可以通过对应的Looper方便的实现线程间通信。