Launcher3中wait、notify应用场景分析

编程入门 行业动态 更新时间:2024-10-16 16:20:30

Launcher3中wait、notify应用<a href=https://www.elefans.com/category/jswz/34/1770727.html style=场景分析"/>

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应用场景分析

本文发布于:2024-02-06 09:28:46,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1748184.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:场景   wait   notify

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!