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

81 篇文章 3 订阅

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),这一次事件传递就完成了。

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值