HandlerThread的使用
首先承接上一篇的HandlerThread类,先来看看HandlerThread的源码
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
......
@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;
}
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;
}
public int getThreadId() {
return mTid;
}
}
通过源码可以看到HandlerThread在run方法中创建了一个Looper,当run()方法执行以后我们可以通过getLooper方法得到在子线程中创建的Looper
因为Handler的post方法要执行的run()方法中的逻辑,而这个逻辑执行的线程是在创建Handler时对应的Looper创建的线程中执行的,所以这个Looper一定要在子线程中创建。在来看看系统是怎么开启的子线程的
public static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
static {
sWorkerThread.start();
}
public static final Handler sWorker = new Handler(sWorkerThread.getLooper());
可以看到通过静态代码块先开启HandlerThread的子线程,此时子线程的Looper创建完成,然后尽可以创建Handler,执行post方法
使用HandlerThread这个类有什么好处呢?就是我们直接可以使用Handler的post方法来开启子线程。
Handler的线程闲时调用
在Launcher中有一个类似Handler的类,系统会安排当Handler所在线程闲时的时候才会去执行这个方法中的消息。
代码:
public class DeferredHandler {
@Thunk
LinkedList mQueue = new LinkedList<>();
private MessageQueue mMessageQueue = Looper.myQueue();
private Impl mHandler = new Impl();
@Thunk class Impl extends Handler implements MessageQueue.IdleHandler {
public void handleMessage(Message msg) {
Runnable r;
synchronized (mQueue) {
if (mQueue.size() == 0) {
return;
}
r = mQueue.removeFirst();
}
r.run();
synchronized (mQueue) {
scheduleNextLocked();
}
}
public boolean queueIdle() {
handleMessage(null);
return false;
}
}
private class IdleRunnable implements Runnable {
Runnable mRunnable;
IdleRunnable(Runnable r) {
mRunnable = r;
}
public void run() {
mRunnable.run();
}
}
public DeferredHandler() {
}
/** Schedule runnable to run after everything that's on the queue right now. */
public void post(Runnable runnable) {
synchronized (mQueue) {
mQueue.add(runnable);
if (mQueue.size() == 1) {
scheduleNextLocked();
}
}
}
/** Schedule runnable to run when the queue goes idle. */
public void postIdle(final Runnable runnable) {
post(new IdleRunnable(runnable));
}
public void cancelAll() {
synchronized (mQueue) {
mQueue.clear();
}
}
/** Runs all queued Runnables from the calling thread. */
public void flush() {
LinkedList queue = new LinkedList<>();
synchronized (mQueue) {
queue.addAll(mQueue);
mQueue.clear();
}
for (Runnable r : queue) {
r.run();
}
}
void scheduleNextLocked() {
if (mQueue.size() > 0) {
Runnable peek = mQueue.getFirst();
if (peek instanceof IdleRunnable) {
mMessageQueue.addIdleHandler(mHandler);
} else {
mHandler.sendEmptyMessage(1);
}
}
}
}
这个类中的post方法和一般的Handler的方法一样,会把消息放到消息队列中,当执行到的时候就会执行。
postIdle()方法,系统会安排当Handler所在线程闲时的时候才会去执行这个方法中的消息。可以利用这个特性,将一些耗时的任务通过这种方式来执行。使我们的应用程序更流畅。
这里需要强调的是,因为postIdIe中的消息是执行在调用这个方法的Handler创建的线程中的,所以执行在哪个线程要看的是DeferredHandler对象的创建线程。都是闲时调用。
cancelAll()方法用来清除消息队列中的所有消息
用法:直接new DeferredHandler对象,调用post方法就可以
线程等待
在LoaderTask加载数据的时候,有这样的情况,在开启子线程加载数据的时候,run方法中既要执行加载桌面数据和绑定桌面数据的操作还要执行加载应用菜单列表的数据和绑定应用菜单列表的数据。绑定桌面数据的操作是在UI线程中完成的,系统要保证界面绑定任务可以高效完成,往往是将自己的任务暂停下来等待UI线程处理完成。系统是使用这个方法来做到线程等待的waitForIdle();
实现代码:
private void waitForIdle() {
// Wait until the either we're stopped or the other threads are done.
// This way we don't start loading all apps until the workspace has settled
// down.
synchronized (LoaderTask.this) {
mHandler.postIdle(new Runnable() {
public void run() {
synchronized (LoaderTask.this) {
mLoadAndBindStepFinished = true;
LoaderTask.this.notify();
}
}
});
while (!mStopped && !mLoadAndBindStepFinished && !mFlushingWorkerThread) {
try {
// Just in case mFlushingWorkerThread changes but we aren't woken up,
// wait no longer than 1sec at a time
this.wait(1000);
} catch (InterruptedException ex) {
// Ignore
}
}
}
}
再来重新看看LoaderTask的run方法部分代码:
public void run() {
....
//加载和绑定桌面数据
loadAndBindWorkspace();
....
//等待UI线程执行完毕
waitForIdle();
.....
//加载和绑定应用菜单列表数据
loadAndBindAllApps();
.....
}
postIdle方法是一个UI线程闲时执行的任务,这个任务只有当UI线程闲置下来的时候才会得到执行。在这个方法中改变了一个标志位,来控制下面while无线等待循环(当然,在自己使用这个方法的时候,while的判断条件可以自己控制)。
执行流程分析:当程序走到这个方法的时候,由于mHandler是在主线程创建的,所以Handler会把run方法中的逻辑发送到主线程,但是postIdle方法是UI线程闲时执行的任务,此时UI线程正在执行绑定桌面数据的任务,所以在UI线程忙的时候不会执行这个方法,程序会走到while循环处,经过判断条件进入循环,程序等待。因为使用的wait()方法,所以子线程一直会等在这里。当UI线程闲下来,postIdle方法执行,标志位被置为true同时执行notify方法,唤醒子线程同时跳出循环。程序继续向下执行,进入下面的加载应用菜单数据和绑定数据的逻辑。
总结的三个方法都是关于线程的,可以在自己的应用中如果有对应的应用场景的话可以试一试。
欢迎关注我的微信公众号,我会把一些生活的感想和投资方面的总结写到公众号,希望你能来和我一起交流技术之外的东西。