android 最近任务多进程调度逻辑分析

来源:互联网 发布:淘宝买钢珠警察会查么 编辑:程序博客网 时间:2024/06/05 20:03

android自从把最近任务改为一个activity后,最近任务的内部逻辑的复杂程度就在不停地快速增长着。android是支持多用户的,最近任务在每个用户空间都有一个单独运行的进程。而只有主用户空间的SystemUI进程才能收到PhoneWindowManager发过来的事件,比如showRecents,hideRecents等,所以副用户空间的systemui进程就需要主用户空间的systemui来通知副用户的systemui来做显示最近任务和隐藏最近任务等操作。这就需要有一些调度逻辑了,到android 7.0后,google将原本通过的broadcast来调度的逻辑改为了用service调用,更加难以理清,在这篇博客里,我来单独分析一下最近任务对于多用户多进程的调度逻辑。

1.首先,system_server在系统刚刚启动时,就显示的把systemui里的SystemUIService启动

    static final void startSystemUi(Context context) {        Intent intent = new Intent();        intent.setComponent(new ComponentName("com.android.systemui",                    "com.android.systemui.SystemUIService"));        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);        //Slog.d(TAG, "Starting service: " + intent);        context.startServiceAsUser(intent, UserHandle.SYSTEM);    }

2.然后SystemUIService启动后,在onCreate里面启动SystemUI里所有的service。
注意,这里所谓的“service”并不是真正的android service,而是sytemui里自定义的一个类型SystemUI.class
所有的systemui都继承这个类型,并且在startServicesIfNeeded里面实例化各个service,然后调用相应service的start方法来做初始化操作。

    @Override    public void onCreate() {        super.onCreate();        ((SystemUIApplication) getApplication()).startServicesIfNeeded();    }

3.这里只看Recents.class这个service,在Recents.class的start方法里,有如下一段代码。

    if (sSystemServicesProxy.isSystemUser(processUser)) {        // For the system user, initialize an instance of the interface that we can pass to the        // secondary user        mSystemToUserCallbacks = new RecentsSystemUser(mContext, mImpl);    } else {        // For the secondary user, bind to the primary user's service to get a persistent        // interface to register its implementation and to later update its state        registerWithSystemUser();    }

先判断当前进程是否是主用户进程,如果是主用户,就实例化一个RecentsSystemUser类。
这个类继承IRecentsSystemUserCallbacks这个AIDL,并且传入的参数是一个context和一个RecentsImpl mImpl,这个RecentsImpl里有最近任务许多功能的具体实现。

/** * An implementation of the system user's Recents interface to be called remotely by secondary * users. */public class RecentsSystemUser extends IRecentsSystemUserCallbacks.Stub {
package com.android.systemui.recents;import android.graphics.Rect;/** * Due to the fact that RecentsActivity is per-user, we need to establish an * interface (this) for the non-system user to register itself for callbacks and to * callback to the system user to update internal state. */oneway interface IRecentsSystemUserCallbacks {    void registerNonSystemUserCallbacks(IBinder nonSystemUserCallbacks, int userId);    void updateRecentsVisibility(boolean visible);    void startScreenPinning(int taskId);    void sendRecentsDrawnEvent();    void sendDockingTopTaskEvent(int dragMode, in Rect initialRect);    void sendLaunchRecentsEvent();}

这个AIDL是用于副用户进程的最近任务想主用户进程发送事件的。

4.如果没有副用户启动的话,这部分逻辑就已经结束了。
如果有副用户进程的systemui启动的话,就需要让副用户进程的systemui来和之前这个mSystemToUserCallbacks取得联系。最直接的方式,让副用户进程绑定一个主用户进程的service,
这个service的名字是RecentsSystemUserService。

/** * A strictly system-user service that is started by the secondary user's Recents (with a limited * lifespan), to get the interface that the secondary user's Recents can call through to the system * user's Recents. */public class RecentsSystemUserService extends Service {    private static final String TAG = "RecentsSystemUserService";    private static final boolean DEBUG = false;    @Override    public void onCreate() {        super.onCreate();    }    @Override    public IBinder onBind(Intent intent) {        SystemUIApplication app = (SystemUIApplication) getApplication();        Recents recents = app.getComponent(Recents.class);        if (DEBUG) {            Log.d(TAG, "onBind: " + recents);        }        if (recents != null) {            return recents.getSystemUserCallbacks();        }        return null;    }

5.现在来分析副用户进程的Recents的启动逻辑。
在切换用户的时候,systemui会监听android.intent.action.USER_SWITCHED这个广播,接收到这个广播后,会在副用户空间启动一个新的service,SystemUISecondaryUserService,通过启动这个service,启动了一个新的进程。这部分逻辑在UserSwitcherController这个类里,代码在下面,就不具体分析了。

if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {     if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {         mExitGuestDialog.cancel();         mExitGuestDialog = null;     }     final int currentId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);     final UserInfo userInfo = mUserManager.getUserInfo(currentId);     final int N = mUsers.size();     for (int i = 0; i < N; i++) {         UserRecord record = mUsers.get(i);         if (record.info == null) continue;         boolean shouldBeCurrent = record.info.id == currentId;         if (record.isCurrent != shouldBeCurrent) {             mUsers.set(i, record.copyWithIsCurrent(shouldBeCurrent));         }         if (shouldBeCurrent && !record.isGuest) {             mLastNonGuestUser = record.info.id;         }         if ((userInfo == null || !userInfo.isAdmin()) && record.isRestricted) {             // Immediately remove restricted records in case the AsyncTask is too slow.             mUsers.remove(i);             i--;         }     }     notifyAdapters();     // Disconnect from the old secondary user's service     if (mSecondaryUser != UserHandle.USER_NULL) {         context.stopServiceAsUser(mSecondaryUserServiceIntent,                 UserHandle.of(mSecondaryUser));         mSecondaryUser = UserHandle.USER_NULL;     }     // Connect to the new secondary user's service (purely to ensure that a persistent     // SystemUI application is created for that user)     if (userInfo != null && !userInfo.isPrimary()) {         context.startServiceAsUser(mSecondaryUserServiceIntent,                 UserHandle.of(userInfo.id));         mSecondaryUser = userInfo.id;     }     if (UserManager.isSplitSystemUser() && userInfo != null && !userInfo.isGuest()             && userInfo.id != UserHandle.USER_SYSTEM) {         showLogoutNotification(currentId);     }     if (userInfo != null && userInfo.isGuest()) {         showGuestNotification(currentId);     }     unpauseRefreshUsers = true; }

6.上面启动了SystemUISecondaryUserService这个service后,这个service的onCreate里面去启动分用户的Recents,和主用户空间的逻辑类似,只是在副用户的systemui进程里,只有两个SystemUI的实例。

    /**     * The classes of the stuff to start for each user.  This is a subset of the services listed     * above.     */    private final Class<?>[] SERVICES_PER_USER = new Class[] {            com.android.systemui.recents.Recents.class,            com.android.systemui.tv.pip.PipUI.class    };

现在,Recents.class已经有两个实例了,分别在两个用户的SystemUI进程中。

7.副用户进程的Recents,执行start方法后,这次判断自身不是主用户了,会执行registerWithSystemUser方法。

    /**     * Attempts to register with the system user.     */    private void registerWithSystemUser() {        final int processUser = sSystemServicesProxy.getProcessUser();        postToSystemUser(new Runnable() {            @Override            public void run() {                try {                    mUserToSystemCallbacks.registerNonSystemUserCallbacks(                            new RecentsImplProxy(mImpl), processUser);                } catch (RemoteException e) {                    Log.e(TAG, "Failed to register", e);                }            }        });    }    /**     * Runs the runnable in the system user's Recents context, connecting to the service if     * necessary.     */    private void postToSystemUser(final Runnable onConnectRunnable) {        mOnConnectRunnables.add(onConnectRunnable);        if (mUserToSystemCallbacks == null) {            Intent systemUserServiceIntent = new Intent();            systemUserServiceIntent.setClass(mContext, RecentsSystemUserService.class);            boolean bound = mContext.bindServiceAsUser(systemUserServiceIntent,                    mUserToSystemServiceConnection, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM);            EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,                    EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_BIND_SERVICE,                    sSystemServicesProxy.getProcessUser());            if (!bound) {                // Retry after a fixed duration                mHandler.postDelayed(new Runnable() {                    @Override                    public void run() {                        registerWithSystemUser();                    }                }, BIND_TO_SYSTEM_USER_RETRY_DELAY);            }        } else {            runAndFlushOnConnectRunnables();        }    }    /**     * Runs all the queued runnables after a service connection is made.     */    private void runAndFlushOnConnectRunnables() {        for (Runnable r : mOnConnectRunnables) {            r.run();        }        mOnConnectRunnables.clear();    }

8.registerWithSystemUser这个方法的作用是与主用户空间的mSystemToUserCallbacks建立联系。先要执行
postToSystemUser方法,并且把mUserToSystemCallbacks.registerNonSystemUserCallbacks(
new RecentsImplProxy(mImpl), processUser);作为一个Runnable传进去。
一点点分析吧。
这个postToSystemUser方法里,先把参数里的Runnable放到一个全局变量里,然后在mUserToSystemCallbacks不为空的时候调用runAndFlushOnConnectRunnables来把这些Runnable执行并清空。
而mUserToSystemCallbacks是在onServiceConnected里赋值的。总结起来就是绑定service后会把之前的Runnable执行并清空。而这个要绑定的服务就是主用户进程里的RecentsSystemUserService。

再来看这个绑定service传入的ServiceConnection是怎么写的。

    // Only for secondary users, this is the service connection we use to connect to the system user    private final ServiceConnection mUserToSystemServiceConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            if (service != null) {                mUserToSystemCallbacks = IRecentsSystemUserCallbacks.Stub.asInterface(                        service);                EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,                        EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_SYSTEM_BOUND,                        sSystemServicesProxy.getProcessUser());                // Listen for system user's death, so that we can reconnect later                try {                    service.linkToDeath(mUserToSystemCallbacksDeathRcpt, 0);                } catch (RemoteException e) {                    Log.e(TAG, "Lost connection to (System) SystemUI", e);                }                // Run each of the queued runnables                runAndFlushOnConnectRunnables();            }            // Unbind ourselves now that we've registered our callbacks.  The            // binder to the system user are still valid at this point.            mContext.unbindService(this);        }        @Override        public void onServiceDisconnected(ComponentName name) {            // Do nothing        }    };

这个mUserToSystemCallbacks = IRecentsSystemUserCallbacks.Stub.asInterface(
service);可以看出,mUserToSystemCallbacks是RecentsSystemUserService的onBind返回值,
从之前贴过的源码里,可以看到onBind的返回值,是recents.getSystemUserCallbacks(),这个值就是主用户进程里的mSystemToUserCallbacks。所以mUserToSystemCallbacks在这里就被赋值为了mSystemToUserCallbacks(当然不是直接赋值,是一个binder代理)。

所以总结一下这部分的逻辑,就是副用户进程的systemui启动后,绑定主用户进程的RecentsSystemUserService,通过绑定这个service的返回值,拿到了主用户进程里的mSystemToUserCallbacks的代理。再通过这个代理,调用主用户空间的registerNonSystemUserCallbacks,把自身的RecentsImplProxy传到主用户进程去。这样,两个进程就分别拿到对方进程的一个代理,就可以通过这两个代理来相互调用了。
绑定成功后,副用户进程会调用runAndFlushOnConnectRunnables把之前存的Runnable执行并清空,这里的Runnable会有很多,不仅仅是这一个注册的Runnable,还有有最近任务显示状态的变化,开启分屏等等的Runnable。
还通过linkToDeath做了断线重连,重连时间是5秒。

registerNonSystemUserCallbacks的源码在下面。

    @Override    public void registerNonSystemUserCallbacks(final IBinder nonSystemUserCallbacks,            final int userId) {        try {            final IRecentsNonSystemUserCallbacks callback =                    IRecentsNonSystemUserCallbacks.Stub.asInterface(nonSystemUserCallbacks);            nonSystemUserCallbacks.linkToDeath(new IBinder.DeathRecipient() {                @Override                public void binderDied() {                    mNonSystemUserRecents.removeAt(mNonSystemUserRecents.indexOfValue(callback));                    EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,                            EventLogConstants.SYSUI_RECENTS_CONNECTION_SYSTEM_UNREGISTER_USER,                            userId);                }            }, 0);            mNonSystemUserRecents.put(userId, callback);            EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,                    EventLogConstants.SYSUI_RECENTS_CONNECTION_SYSTEM_REGISTER_USER, userId);        } catch (RemoteException e) {            Log.e(TAG, "Failed to register NonSystemUserCallbacks", e);        }    }

把副用户的代理SparseArray mNonSystemUserRecents保存在这个列表里,以用户id为key,代理为value。

加深一下记忆,这个时候,主用户进程拿到的副用户代理保存在mSystemToUserCallbacks里的mNonSystemUserRecents列表里,可以通过mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser)得到。代理的实际对象是副用户进程里在registerWithSystemUser方法里的一个new RecentsImplProxy(mImpl)。
副用户进程拿到的主用户代理是Recents里的mUserToSystemCallbacks。代理的实际对象是主用户进程的mSystemToUserCallbacks,是通过new RecentsSystemUser(mContext, mImpl)得到的。



下面来实际分析一个调度过程

在副用户空间里,按MENU键启动最近任务。

主用户进程向副用户进程传递事件

主用户进程先从PhoneWindowManager里获得toggleRecents事件,经过很多层的调用后,传到Recents里的toggleRecents方法来,这个时候判断当前用户是副用户,主用户进程就调用 IRecentsNonSystemUserCallbacks callbacks =
mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);拿到副用户的代理,然后调用callbacks.toggleRecents(growTarget);来通知副用户该启动最近任务了。
副用户进程的RecentsImplProxy收到toggleRecents事件,会调用副用户进程里的RecentsImpl的toggleRecents
方法。然后又经过一些处理,调用了 mContext.startActivityAsUser(intent, UserHandle.CURRENT);
来启动RecentsActivity。

副用户进程向主用户进程传递事件

RecentsActivity在触发onStart后,会发送一个RecentsVisibilityChangedEvent事件,这里用了EventBus,Recents里有一个接受RecentsVisibilityChangedEvent事件的地方,在这里判断如果本进程是副用户进程,就执行mUserToSystemCallbacks.updateRecentsVisibility(event.visible)。就触发了主用户进程里mSystemToUserCallbacks的updateRecentsVisibility,再经过一些调用调到了mIwm.setRecentsVisibility(visible),这一次事件传递就完成了。

本来只是解决一个问题想要顺带写写博客,结果稍微一写就一大篇,继续解决问题去了!

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 淘宝买家恶意差评怎么办 淘宝评价完了忘截图了怎么办 12306app登录不上怎么办 淘宝换绑支付宝失败怎么办 淘宝和手机不兼容怎么办 换号之后微信怎么办 手机不兼容的应用程序怎么办 微信版本低登录不了怎么办 软件与系统不兼容怎么办 软件与手机系统不兼容怎么办 qq和手机不兼容怎么办 来个软件不兼容怎么办 安卓8.0不兼容app怎么办 两条内存不兼容怎么办 王者荣耀软件不兼容怎么办 冒险岛不兼容win7怎么办 百度网盘手机号换了怎么办 破解版游戏闪退怎么办 安卓手机软件不兼容怎么办 安卓8.0软件闪退怎么办 游戏与手机系统不兼容怎么办 耳机和手机不兼容怎么办 软件和手机不兼容怎么办 小米6开关键失灵怎么办 同步助手下载不了微信旧版本怎么办 闲鱼退货卖家拒收怎么办 闲鱼把联系人删了怎么办 闲鱼付款了卖家不发货怎么办 红米4c卡怎么办 如果买鞋子买到假的怎么办 猎趣永久封号钱怎么办 支付宝换绑定手机后怎么办 为什么回收站的删除键不见了怎么办 微信在异地登录怎么办 支付宝帐号被冻结怎么办 进不了路由器设置页面怎么办 支付宝支付密码忘记了怎么办 淘宝忘记登录密码了怎么办 手机换卡了微信怎么办 淘宝退款成功后收到货怎么办 没收到货退款商家不处理怎么办