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

android 最近任务多进程调度逻辑分析android 自从把最近任务改为一个 activity 后 最近任务的内部逻辑的复杂程度就在不停地快速增长着 android 是支持多用户的 最近任务在每个用户空间都有一个单独运行的进程 而只有主用户空间的 SystemUI 进程才能收到 PhoneWindowM 发过来的事件 比如 showRecents

大家好,我是讯享网,很高兴认识大家。

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(); }
讯享网/ * 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; }
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。



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

在副用户空间里,按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),这一次事件传递就完成了。

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

小讯
上一篇 2025-01-19 13:52
下一篇 2025-01-04 23:08

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/36814.html