您的位置:首页 > 移动开发 > Android开发

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

2017-09-08 15:47 288 查看
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),这一次事件传递就完成了。

本来只是解决一个问题想要顺带写写博客,结果稍微一写就一大篇,继续解决问题去了!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐