由于篇幅问题第一篇介绍了栈的大管家,这篇介绍stack,ActivityStack类也比较恐怖有五千行代码,不过ActivityStack主要负对task的管理,对activity的管理,为ActivityStackSupervisor做了很多更底层的事情,对整个activity的管理最重要和繁多的一个环节,我们要想学习wms必须先搞清楚stack的逻辑,再搞清楚task的逻辑,最后将ams的情景进行总结,根据不同的情景再次走一次,加深印象。
首先还是先看stack维护的数据结构
enum ActivityState {
INITIALIZING,
RESUMED,
PAUSING,
PAUSED,
STOPPING,
STOPPED,
FINISHING,
DESTROYING,
DESTROYED
}
- 从ActivityState这个枚举值可以看到,一个actviity的生命周期包括上面的9个状态,stack正式负责这九个状态的切换。
- stack的可见状态如下
//不可见
static final int STACK_INVISIBLE = 0;
// 可见
static final int STACK_VISIBLE = 1;
//可见但是被其他stack遮挡,如透明的activity
static final int STACK_VISIBLE_ACTIVITY_BEHIND = 2;
- 移除task的模式也有三种
// 销毁
static final int REMOVE_TASK_MODE_DESTROYING = 0;
//移动,不执行一些操作,例如不从wms中移除或者最近任务移除
static final int REMOVE_TASK_MODE_MOVING = 1;
// 把task移动到stack顶部,把stack启动到前台
static final int REMOVE_TASK_MODE_MOVING_TO_TOP = 2;
- private final ArrayList mTaskHistory = new ArrayList<>(); task的记录
- final ArrayList mValidateAppTokens = new ArrayList<>();
- final ArrayList mLRUActivities = new ArrayList<>(); 最近最少使用的activity
- final ArrayList mNoAnimActivities = new ArrayList<>(); 不执行activity切换动画的activity
- ActivityRecord mPausingActivity = null; 正在执行pause的activity
- ActivityRecord mLastPausedActivity = null; 最后一个执行pause的activity
- ActivityRecord mLastNoHistoryActivity = null; 用户指定不显示在历史记录的activity
- ActivityRecord mResumedActivity = null; 当先正在显示的activity
- boolean mFullscreen = true; 堆栈是否覆盖整个屏幕
- Rect mBounds = null; stack的大小,全屏时候为空
- boolean mUpdateBoundsDeferred; 延迟更新边界
- boolean mUpdateBoundsDeferredCalled; 延迟更新被调用
final Rect mDeferredBounds = new Rect();
final Rect mDeferredTaskBounds = new Rect();
final Rect mDeferredTaskInsetBounds = new Rect();
同样Stack中也存在一个Handler,运行在AMS的mainHandler所在的线程中,主要的工作还是对超时的处理
final class ActivityStackHandler extends Handler {
ActivityStackHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case PAUSE_TIMEOUT_MSG: { //pause activity超时
ActivityRecord r = (ActivityRecord)msg.obj;
// We don't at this point know if the activity is fullscreen,
// so we need to be conservative and assume it isn't.
Slog.w(TAG, "Activity pause timeout for " + r);
synchronized (mService) {
if (r.app != null) {
mService.logAppTooSlow(r.app, r.pauseTime, "pausing " + r);
}
activityPausedLocked(r.appToken, true);
}
} break;
case LAUNCH_TICK_MSG: { // 启动时间太长 记录log,user版本不会打印
ActivityRecord r = (ActivityRecord)msg.obj;
synchronized (mService) {
if (r.continueLaunchTickingLocked()) {
mService.logAppTooSlow(r.app, r.launchTickTime, "launching " + r);
}
}
} break;
case DESTROY_TIMEOUT_MSG: {
//销毁超时
ActivityRecord r = (ActivityRecord)msg.obj;
// We don't at this point know if the activity is fullscreen,
// so we need to be conservative and assume it isn't.
Slog.w(TAG, "Activity destroy timeout for " + r);
synchronized (mService) {
activityDestroyedLocked(r != null ? r.appToken : null, "destroyTimeout");
}
} break;
case STOP_TIMEOUT_MSG: { // 停止超时
ActivityRecord r = (ActivityRecord)msg.obj;
// We don't at this point know if the activity is fullscreen,
// so we need to be conservative and assume it isn't.
Slog.w(TAG, "Activity stop timeout for " + r);
synchronized (mService) {
if (r.isInHistory()) {
activityStoppedLocked(r, null, null, null);
}
}
} break;
case DESTROY_ACTIVITIES_MSG: { //执行销毁activity
ScheduleDestroyArgs args = (ScheduleDestroyArgs)msg.obj;
synchronized (mService) {
destroyActivitiesLocked(args.mOwner, args.mReason);
}
} break;
case TRANSLUCENT_TIMEOUT_MSG: {
//设置半透明超时
synchronized (mService) {
notifyActivityDrawnLocked(null);
}
} break;
case RELEASE_BACKGROUND_RESOURCES_TIMEOUT_MSG: { //取消后台显示超时
synchronized (mService) {
final ActivityRecord r = getVisibleBehindActivity();
Slog.e(TAG, "Timeout waiting for cancelVisibleBehind player=" + r);
if (r != null) {
mService.killAppAtUsersRequest(r.app, null);
}
}
} break;
}
}
}
这一又一次验证了系统服务不能保证客户端高效可靠的执行,全部用超时处理
- int numActivities() 函数返回stack中有多少个activity
- 下面来看一下构造函数
ActivityStack(ActivityStackSupervisor.ActivityContainer activityContainer,
RecentTasks recentTasks) {
mActivityContainer = activityContainer; //所在的activity container
mStackSupervisor = activityContainer.getOuter(); // 栈大管家赋值
mService = mStackSupervisor.mService; //AMS
mHandler = new ActivityStackHandler(mService.mHandler.getLooper()); //Handler创建
mWindowManager = mService.mWindowManager; //WMS赋值
mStackId = activityContainer.mStackId; // stackid
mCurrentUser = mService.mUserController.getCurrentUserIdLocked(); //当前user
mRecentTasks = recentTasks; //recent task
mTaskPositioner = mStackId == FREEFORM_WORKSPACE_STACK_ID
? new LaunchingTaskPositioner() : null; //freeform stack需要使用LaunchingTaskPositioner计算位置
}
在看看stack创建和销毁的操作
stack创建后会马上attach display上面
void attachDisplay(ActivityStackSupervisor.ActivityDisplay activityDisplay, boolean onTop) {
mDisplayId = activityDisplay.mDisplayId;
mStacks = activityDisplay.mStacks;
//mBounds会通过WMS返回,wms会根据docker stack情况计算出新的stack大小
mBounds = mWindowManager.attachStack(mStackId, activityDisplay.mDisplayId, onTop);
mFullscreen = mBounds == null;
if (mTaskPositioner != null) {
// freedom stack的一些处理
mTaskPositioner.setDisplay(activityDisplay.mDisplay);
mTaskPositioner.configure(mBounds);
}
if (mStackId == DOCKED_STACK_ID) {
//更新docker stack大小
// If we created a docked stack we want to resize it so it resizes all other stacks
// in the system.
mStackSupervisor.resizeDockedStackLocked(
mBounds, null, null, null, null, PRESERVE_WINDOWS);
}
}
//从display上移除stack
void detachDisplay() {
mDisplayId = Display.INVALID_DISPLAY;
mStacks = null;
if (mTaskPositioner != null) {
mTaskPositioner.reset();
}
//WMS先移除
mWindowManager.detachStack(mStackId);
if (mStackId == DOCKED_STACK_ID) { //更新其他 stack大小
// If we removed a docked stack we want to resize it so it resizes all other stacks
// in the system to fullscreen.
mStackSupervisor.resizeDockedStackLocked(
null, null, null, null, null, PRESERVE_WINDOWS);
}
}
后面一些列deferUpdateBounds的操作的作用是在activity启动的时候不去更新HOME STACK的大小,直到app动画执行完成的时候再去更新,其中有三个函数,void deferUpdateBounds()表示开始执行延迟更新,而在resize的过程中,先不更新stack的边界,使用boolean updateBoundsAllowed(Rect bounds, Rect tempTaskBounds,
Rect tempTaskInsetBounds)函数记录边界,并且设置mUpdateBoundsDeferredCalled = true;表示边界有更新,最后在activity切换动画执行完成后,使用void continueUpdateBounds()更新stack边界。setBounds函数设置stack的大小
void setBounds(Rect bounds) {
mBounds = mFullscreen ? null : new Rect(bounds);
if (mTaskPositioner != null) {
mTaskPositioner.configure(bounds);
}
}
- final ActivityRecord topRunningActivityLocked() 返回最stack最上面没有finish并且user可以显示的activity
- final ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) 返回不延迟显示的activity
- final ActivityRecord topActivity() 返回没有finish的activity,不管user是不是可以显示
- final TaskRecord topTask()
- TaskRecord taskForIdLocked(int id) 根据id找task
- ActivityRecord isInStackLocked(ActivityRecord r) 返回activity是否在stack中
- moveToFront函数把stack移动到前台,并且如果指定了tsak参数,把task移动到前台
/**
* @param reason The reason for moving the stack to the front.
* @param task If non-null, the task will be moved to the top of the stack.
* */
void moveToFront(String reason, TaskRecord task) {
if (!isAttached()) {
//1 没有attach不操作
return;
}
mStacks.remove(this);
int addIndex = mStacks.size();
if (addIndex > 0) {
final ActivityStack topStack = mStacks.get(addIndex - 1);
if (StackId.isAlwaysOnTop(topStack.mStackId) && topStack != this) {
//2 画中画的stack永远在最上面,添加到倒数第二个的位置
// If the top stack is always on top, we move this stack just below it.
addIndex--;
}
}
//3 添加到计算出的位置
mStacks.add(addIndex, this);
// TODO(multi-display): Needs to also work if focus is moving to the non-home display.
if (isOnHomeDisplay()) { //4 设置为焦点stack
mStackSupervisor.setFocusStackUnchecked(reason, this);
}
if (task != null) {
insertTaskAtTop(task, null);
} else {
task = topTask();
}
//4移动task到顶部,通过WMS移动window
if (task != null) {
mWindowManager.moveTaskToTop(task.taskId);
}
}
- isFocusable 返回stack是否能够获取焦点,除了画中画所在的stack,其他都默认可以接收事件,获取焦点。 对于画中画所在的stack要看它的top activity能否获取焦点
- findTaskLocked函数比较重要,是在startActivity过程中寻找复用的task过程中使用的函数
void findTaskLocked(ActivityRecord target, FindTaskResult result) {
Intent intent = target.intent;
ActivityInfo info = target.info;
ComponentName cls = intent.getComponent();
if (info.targetActivity != null) {
//1 指定了targetActivity的情况以target为准,
//AndroidManifest中可以配置,一种解耦的手段
cls = new ComponentName(info.packageName, info.targetActivity);
}
//2 document task的话要匹配指定的data,所以这里获取documentData
final int userId = UserHandle.getUserId(info.applicationInfo.uid);
boolean isDocument = intent != null & intent.isDocument();
// If documentData is non-null then it must match the existing task data.
Uri documentData = isDocument ? intent.getData() : null;
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + target + " in " + this);
//3 遍历task,只是匹配task的top activity
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
//4 voiceSession不做考虑
if (task.voiceSession != null) {
// We never match voice sessions; those always run independently.
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": voice session");
continue;
}
//5 user不匹配不做考虑
if (task.userId != userId) {
// Looking for a different task.
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": different user");
continue;
}
//6 空task,finish的activity和user不匹配,LAUNCH_SINGLE_INSTANCE的activity不匹配。LAUNCH_SINGLE_INSTANCE的情况使用另外一个函数寻找
final ActivityRecord r = task.getTopActivity();
if (r == null || r.finishing || r.userId != userId ||
r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": mismatch root " + r);
continue;
}
//7 类型不匹配的跳过
if (r.mActivityType != target.mActivityType) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": mismatch activity type");
continue;
}
final Intent taskIntent = task.intent;
final Intent affinityIntent = task.affinityIntent;
final boolean taskIsDocument;
final Uri taskDocumentData;
//8 计算task的document模式和data
if (taskIntent != null && taskIntent.isDocument()) {
taskIsDocument = true;
taskDocumentData = taskIntent.getData();
} else if (affinityIntent != null && affinityIntent.isDocument()) {
taskIsDocument = true;
taskDocumentData = affinityIntent.getData();
} else {
taskIsDocument = false;
taskDocumentData = null;
}
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Comparing existing cls="
+ taskIntent.getComponent().flattenToShortString()
+ "/aff=" + r.task.rootAffinity + " to new cls="
+ intent.getComponent().flattenToShortString() + "/aff=" + info.taskAffinity);
// TODO Refactor to remove duplications. Check if logic can be simplified.
//9 先根据Component查找,优先级最高
if (taskIntent != null && taskIntent.getComponent() != null &&
taskIntent.getComponent().compareTo(cls) == 0 &&
Objects.equals(documentData, taskDocumentData)) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching class!");
//dump();
if (DEBUG_TASKS) Slog.d(TAG_TASKS,
"For Intent " + intent + " bringing to top: " + r.intent);
result.r = r;
result.matchedByRootAffinity = false;
break;
//10 按照粘性复用,必须要匹配document data
} else if (affinityIntent != null && affinityIntent.getComponent() != null &&
affinityIntent.getComponent().compareTo(cls) == 0 &&
Objects.equals(documentData, taskDocumentData)) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching class!");
//dump();
if (DEBUG_TASKS) Slog.d(TAG_TASKS,
"For Intent " + intent + " bringing to top: " + r.intent);
result.r = r;
result.matchedByRootAffinity = false;
break;
//11 根据rootAffinity匹配,也就是根节点的资源粘性,这种情况不跳出循环,默认的taskAffinity就是包名
} else if (!isDocument && !taskIsDocument
&& result.r == null && task.canMatchRootAffinity()) {
if (task.rootAffinity.equals(target.taskAffinity)) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching affinity candidate!");
// It is possible for multiple tasks to have the same root affinity especially
// if they are in separate stacks. We save off this candidate, but keep looking
// to see if there is a better candidate.
result.r = r;
result.matchedByRootAffinity = true;
}
} else if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Not a match: " + task);
}
}
- 这个就是findActivityLocked用于寻找可以复用的activity
ActivityRecord findActivityLocked(Intent intent, ActivityInfo info,
boolean compareIntentFilters) {
//1 计算目标 ComponentName
ComponentName cls = intent.getComponent();
if (info.targetActivity != null) {
cls = new ComponentName(info.packageName, info.targetActivity);
}
final int userId = UserHandle.getUserId(info.applicationInfo.uid);
//2 遍历task
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
final boolean notCurrentUserTask =
!mStackSupervisor.isCurrentProfileLocked(task.userId);
final ArrayList<Activ