Launcher3之HandlerThread应用场景分析

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方便的实现线程间通信。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值