场景分析"/>
Launcher3中wait、notify应用场景分析
wait、notify方法介绍
wait和notify是Java中的两个方法,可以用于线程间的协作,最典型的应用就是生产和消费者模式。不过,没有真正用过的同学可能会认为它们是Thread或者Runnable的方法,其实不然,它们是Object父类的方法。
先看下Object.java中wait和notify方法的定义:
wait方法:
public final void wait(long millis) throws InterruptedException {wait(millis, 0);}public final native void wait(long millis, int nanos) throws InterruptedException;public final native void wait() throws InterruptedException;
notify方法:
public final native void notify();public final native void notifyAll();
可以看出,wait和notify都是native方法,具体实现可能涉及效率考虑,所以通过native代码去实现了。
Launcher中的应用场景分析
以Launcher启动时,加载数据和绑定界面为例,这里通过wait和notify的使用,保证了UI线程优先执行。
以下是LauncherModel的startLoaderForResults方法,调用后会启动工作线程开始加载和绑定流程
public void startLoaderForResults(LoaderResults results) {synchronized (mLock) {stopLoader();mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, sBgDataModel, results);runOnWorkerThread(mLoaderTask);}}
LoaderTask实现了Runnalbe接口,它的run方法中,就是具体的实现了。从注释可以看到,分了好多步骤去加载数据和绑定界面,执行的任务还是挺繁重的,为了简洁明了的说明问题,这里去掉了后面部分代码,只保留了loadWorkspace、bindWorkspace、loadAllApps和bindAllApps实现代码。
public void run() {synchronized (this) {// Skip fast if we are already stopped.if (mStopped) {return;}}TraceHelper.beginSection(TAG);try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {TraceHelper.partitionSection(TAG, "step 1.1: loading workspace");loadWorkspace();verifyNotStopped();TraceHelper.partitionSection(TAG, "step 1.2: bind workspace workspace");mResults.bindWorkspace();// Notify the installer packages of packages with active installs on the first screen.TraceHelper.partitionSection(TAG, "step 1.3: send first screen broadcast");sendFirstScreenActiveInstallsBroadcast();// Take a breakTraceHelper.partitionSection(TAG, "step 1 completed, wait for idle");waitForIdle();verifyNotStopped();// second stepTraceHelper.partitionSection(TAG, "step 2.1: loading all apps");loadAllApps();TraceHelper.partitionSection(TAG, "step 2.2: Binding all apps");verifyNotStopped();mResults.bindAllApps();......transactionmit();} catch (CancellationException e) {// Loader stopped, ignoreTraceHelper.partitionSection(TAG, "Cancelled");}TraceHelper.endSection(TAG);}
run方法运行在工作线程,mResults.bindWorkspace()方法实现其实是启动的UI线程在绑定界面,虽然run方法是工作线程,可以继续做自己的事情,而且正常不会导致ANR,但是在主线程高负荷刷新和渲染界面的时候,如果存在后台线程也在忙碌运转的话,主线程多少还是会受到影响的,CPU时间片肯定会减少,界面加载时间就会变长,这不是我们愿意看到的。
在Launcher这样的应用中,更是不能容忍,看看上面代码中的注释‘Take a break’,在继续loadAllApps之前,工作线程做了一个暂停,为什么要这样做呢?这其实是在给bindWorkspace让路,延迟loadAllApps,实现线程暂停的就是waitForIdle()方法。
看下waitForIdle()的实现,用到了while循环,看起来还是在做事情,没有wait之类的方法暂停线程,但是调用了LooperIdleLock的awaitLocked(1000)方法
protected synchronized 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.LooperIdleLock idleLock = mResults.newIdleLock(this);// Just in case mFlushingWorkerThread changes but we aren't woken up,// wait no longer than 1sec at a timewhile (!mStopped && idleLock.awaitLocked(1000));}
继续往下看LooperIdleLock的的实现
public class LooperIdleLock implements MessageQueue.IdleHandler, Runnable {private final Object mLock;private boolean mIsLocked;public LooperIdleLock(Object lock, Looper looper) {mLock = lock;mIsLocked = true;if (Utilities.ATLEAST_MARSHMALLOW) {looper.getQueue().addIdleHandler(this);} else {// Looper.myQueue() only gives the current queue. Move the execution to the UI thread// so that the IdleHandler is attached to the correct message queue.new LooperExecutor(looper).execute(this);}}@Overridepublic void run() {Looper.myQueue().addIdleHandler(this);}@Overridepublic boolean queueIdle() {synchronized (mLock) {mIsLocked = false;mLock.notify();}return false;}public boolean awaitLocked(long ms) {if (mIsLocked) {try {// Just in case mFlushingWorkerThread changes but we aren't woken up,// wait no longer than 1sec at a timemLock.wait(ms);} catch (InterruptedException ex) {// Ignore}}return mIsLocked;}
}
awaitLocked方法中调用了mLock.wait(ms),每次调用一次会等待1秒钟,如果while循环判断mIsLocked为true,就会一直等待下去,直到外部线程调用了mLock的notify方法。
全局搜索了launcher的代码后,发现只有两个地方调用了notify方法:
一处是LooperIdleLock的queueIdle方法:
@Overridepublic boolean queueIdle() {synchronized (mLock) {mIsLocked = false;mLock.notify();}return false;}
queueIdle方法是MessageQueue.IdleHandler接口的方法,从上面LooperIdleLock的代码实现可以看出,LooperIdleLock实现了这个接口
public LooperIdleLock(Object lock, Looper looper) {mLock = lock;mIsLocked = true;if (Utilities.ATLEAST_MARSHMALLOW) {looper.getQueue().addIdleHandler(this);} else {// Looper.myQueue() only gives the current queue. Move the execution to the UI thread// so that the IdleHandler is attached to the correct message queue.new LooperExecutor(looper).execute(this);}}
LooperIdleLock在构造函数中,通过looper.getQueue().addIdleHandler(this)往主线程消息队列中注册了这个接口,主线程空闲时,会回调queueIdle方法,进而通知work线程,不用等待了。
另一处是LoaderTask的stopLocked方法:
public synchronized void stopLocked() {mStopped = true;this.notify();}
这个方法是用来停止加载和绑定流程的,结束流程的同时,当然也要结束工作线程中正在执行的任务,停止等待,notify就是用于唤醒线程,让它结束任务。
总结
不知不觉对于Launcher中wait和notify的应用已经分析完毕,应用场景即,在UI线程bindWorkspace时候,通过wait停止线程,等Workspace绑定结束再去loadAllApps,而notify在Workspace绑定结束后会被调用,从而唤醒线程继续执行loadAllApps,这个应用保证了UI线程优先执行。
通观整个AOSP launcher也就这一个地方用到了wait和notify,可见平时我们基本用不到,只有在特定的场景才会遇到。
更多推荐
Launcher3中wait、notify应用场景分析
发布评论