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),这一次事件传递就完成了。
本来只是解决一个问题想要顺带写写博客,结果稍微一写就一大篇,继续解决问题去了!
- android 最近任务多进程调度逻辑分析
- UCOSII任务调度分析
- UCOSII任务调度分析
- skynet任务调度分析
- skynet任务调度分析
- skynet任务调度分析
- skynet任务调度分析
- skynet任务调度分析
- skynet任务调度分析
- 定时任务进程调度处理
- 进程调度分析
- Linux 进程调度分析
- XV6 进程调度分析
- linux进程调度分析
- 进程调度分析
- Linux进程调度分析
- Hadoop调度源码分析 任务调度阶段
- contiki任务调度机制分析
- 搜狗 2018校招 商业基础工程—测试开发工程师练习题
- 解决vi中粘贴多行代码会产生缩进排版问题
- 怎样通过程序使一个物体运动到目标物体
- jni访问数组
- mySQL之多表查询
- android 最近任务多进程调度逻辑分析
- Redis 通信协议
- python 的日志logging模块学习
- leetcode 438. Find All Anagrams in a String
- idea手动编译jfinal项目报错:java.lang.IllegalAccessError: class sun.reflect.GeneratedConstructorAccessor10 ca
- 分类与回归树(CART,Classification And Regression Tree)
- C#学习笔记之通过http请求连接服务器
- android隐藏搜索框边框 隐藏SearchView边框
- UI or Web 是选择UI重构还是web全栈