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 break
            TraceHelper.partitionSection(TAG, "step 1 completed, wait for idle");
            waitForIdle();
            verifyNotStopped();

            // second step
            TraceHelper.partitionSection(TAG, "step 2.1: loading all apps");
            loadAllApps();

            TraceHelper.partitionSection(TAG, "step 2.2: Binding all apps");
            verifyNotStopped();
            mResults.bindAllApps();

			......

            transaction.commit();
        } catch (CancellationException e) {
            // Loader stopped, ignore
            TraceHelper.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 time
        while (!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);
        }
    }

    @Override
    public void run() {
        Looper.myQueue().addIdleHandler(this);
    }

    @Override
    public 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 time
                mLock.wait(ms);
            } catch (InterruptedException ex) {
                // Ignore
            }
        }
        return mIsLocked;
    }
}

awaitLocked方法中调用了mLock.wait(ms),每次调用一次会等待1秒钟,如果while循环判断mIsLocked为true,就会一直等待下去,直到外部线程调用了mLock的notify方法。

全局搜索了launcher的代码后,发现只有两个地方调用了notify方法:
一处是LooperIdleLock的queueIdle方法:

    @Override
    public 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,可见平时我们基本用不到,只有在特定的场景才会遇到。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值