最近由于工作上的需要,结合一些大神的相关博客与Android源码,大概的把WMS中的app Transition流程给熟悉了一遍,在此写下一些小结
app Transition的主要流程
app Transition代表activity组件的切换过程,启动或是退出activity都会执行app Transition,Android系统定义了多达十几种app Transition类型,这些类型定义在AppTransition.java中
app Transition的过程可以用下图表示。
image.png
在apptransition的过程中,当前的activity会被设置为pause状态,同时窗口也将被设置为invisible,而即将启动的activity被设置为resume状态,窗口设置为visible,这个窗口切换的过程会以动画的形式执行,主要涉及的系统服务有AMS和WMS,本文只讨论窗口切换相关的流程,即WMS部分,AMS的不做涉及。
App Transition的核心步骤是定义在WindowManagerService.java中的3个函数完成,这些函数是随着activity的启动流程而逐步执行的。
public void prepareAppTransition(int transit, boolean alwaysKeepCurrent) {…}
通知wms准备窗口切换操作, transit代表具体的切换类型
public void setAppVisibility(IBinder token, boolean visible) {…}
设置activity窗口的可见性
public void executeAppTransition() {…}
执行窗口切换操作,具体的内容是窗口刷新,根据之前的transit参数选择对应的切换动画并播放
可以用一个流程图表示这一过程
image.png
我们来一一分析这几个重要的函数
prepareAppTransition
WindowManagerService.java
public void prepareAppTransition(int transit, boolean alwaysKeepCurrent) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"prepareAppTransition()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
synchronized(mWindowMap) {
boolean prepared = mAppTransition.prepareAppTransitionLocked(
transit, alwaysKeepCurrent);
if (prepared && okToDisplay()) {
mSkipAppTransitionAnimation = false;
}
}
}
这个函数的核心是调到了AppTransition.java中的prepareAppTransitionLocked函数,我们来看看具体实现
AppTransition.java
boolean prepareAppTransitionLocked(int transit, boolean alwaysKeepCurrent) {
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Prepare app transition:"
+ " transit=" + appTransitionToString(transit)
+ " " + this
+ " alwaysKeepCurrent=" + alwaysKeepCurrent
+ " Callers=" + Debug.getCallers(3));
if (!isTransitionSet() || mNextAppTransition == TRANSIT_NONE) {
setAppTransition(transit);
} else if (!alwaysKeepCurrent) {
if (transit == TRANSIT_TASK_OPEN && isTransitionEqual(TRANSIT_TASK_CLOSE)) {
// Opening a new task always supersedes a close for the anim.
setAppTransition(transit);
} else if (transit == TRANSIT_ACTIVITY_OPEN
&& isTransitionEqual(TRANSIT_ACTIVITY_CLOSE)) {
// Opening a new activity always supersedes a close for the anim.
setAppTransition(transit);
}
}
boolean prepared = prepare();
if (isTransitionSet()) {
mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
mService.mH.sendEmptyMessageDelayed(H.APP_TRANSITION_TIMEOUT, APP_TRANSITION_TIMEOUT_MS);
}
return prepared;
}
其中mNextAppTransition 代表了将要执行的activity组件切换类型,此函数做了这么几件事:
1.如果mNextAppTransition 没有被设置或是设置为TRANSIT_NONE的话,就设置为参数所代表的activity组件切换类型
2.如果mNextAppTransition 已经被设置并且不要求保持mNextAppTransition不变的话,open activity和open task的优先级要高于close activity和close task,前者会覆盖后者
3.执行prepare()函数,返回结果prepared代表app Transition是否已经准备完成
4.向WMS发送一个延时5秒的消息,强制app Transition在5秒内完成
setAppVisibility
如前面流程图所说,这个函数的主要功能是设置窗口的可见性并触发窗口的relayout,这个函数比较长,我们只看关键的部分
WindowManagerService.java
public void setAppVisibility(IBinder token, boolean visible) {
synchronized(mWindowMap) {
wtoken = findAppWindowToken(token); // 1. 通过IBinder对象token找到wtoken(AppWindowToken对象,代表对应的activity窗口)
......
mOpeningApps.remove(wtoken);
mClosingApps.remove(wtoken); // 2. 把wtoken从mOpeningApps和mClosingApps两个ArraySet中移除
wtoken.hiddenRequested = !visible; // 3. 重新设置wtoken的可见性
......
if (okToDisplay() && mAppTransition.isTransitionSet()) {.
......
wtoken.mAppAnimator.setDummyAnimation(); // 4. 给wtoken先设置一个哑动画,等待具体执行时再设置合适的动画
......
if (visible) { // 5. 根据参数visible选择将wtoken重新加入mOpeningApps或是mClosingApps
mOpeningApps.add(wtoken);
} else {
mClosingApps.add(wtoken);
}
......
return;
}
......
// 6. 通知上层应用窗口的可见性变化(最终传递到ViewRootImpl并触发窗口的布局)
setTokenVisibilityLocked(wtoken, null, visible, AppTransition.TRANSIT_UNSET, true, wtoken.voiceInteraction);
wtoken.updateReportedVisibilityLocked(); // 7. 通知AMS窗口的可见性变化
}
}
用于设置窗口可见性的函数是setTokenVisibilityLocked,我们后面还会相信分析,现在有个印象即可
executeAppTransition
executeAppTransition是完成前面的步骤后最终执行app Transition的函数
WindowManagerService.java
public void executeAppTransition() {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"executeAppTransition()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
synchronized(mWindowMap) {
if (DEBUG_APP_TRANSITIONS) Slog.w(TAG_WM, "Execute app transition: " + mAppTransition
+ " Callers=" + Debug.getCallers(5));
if (mAppTransition.isTransitionSet()) {
mAppTransition.setReady();
final long origId = Binder.clearCallingIdentity();
try {
mWindowPlacerLocked.performSurfacePlacement();
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
}
从代码中不难看出,executeAppTransition是刷新系统UI,在这个过程中会完成窗口可见性变化,窗口动画的设置和播放等过程,具体的流程放到下一篇文章分析
app Transition时序图
用一张时序图总结一下上面的流程
image.png