本文源代码基于 Android 7.0。
- /base/services/core/java/com/android/server/am/ActivityStack.java
- /base/services/core/java/com/android/server/wm/WindowManagerService.java
- /base/services/core/java/com/android/server/am/ActivityRecord.java
- /base/services/core/java/com/android/server/policy/PhoneWindowManager.java
- /base/core/java/android/view/WindowManagerImpl.java
- /base/core/java/android/view/WindowManagerGlobal.java
- /base/core/java/android/view/ViewRootImpl.java
- /base/services/core/java/com/android/server/wm/Session.java
WindowManageService 运行于 system_server 进程,这篇文章从 WindowManageService 的角度来分析 Activity 的窗口。
目录:
- 预览窗口启动过程
- WindowManager 的 addView() 和 RemoveView()
- 总结
1. 预览窗口启动过程
Activity 启动后,窗口并不是马上就显示出来,而是先显示 starting window,作为 Activity 的预览窗口。
- 1.1 ActivityStack.startActivityLocked()
final void startActivityLocked(ActivityRecord r, boolean newTask, boolean keepCurTransition,
ActivityOptions options) {
TaskRecord rTask = r.task;
final int taskId = rTask.taskId;
if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) {
insertTaskAtTop(rTask, r);
// 将Window相应task移至顶部,调用WindowManagerService
mWindowManager.moveTaskToTop(taskId);
}
TaskRecord task = null;
if (!newTask) {
boolean startIt = true;
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
task = mTaskHistory.get(taskNdx);
if (task.getTopActivity() == null) {
continue;
}
if (task == r.task) {
if (!startIt) {
task.addActivityToTop(r);
r.putInHistory();
// 创建AppWindowToken对象
addConfigOverride(r, task);
if (VALIDATE_TOKENS) {
validateAppTokensLocked();
}
ActivityOptions.abort(options);
return;
}
break;
} else if (task.numFullscreen > 0) {
startIt = false;
}
}
}
task = r.task;
task.addActivityToTop(r);
task.setFrontOfTask();
r.putInHistory();
if (!isHomeStack() || numActivities() > 0) {
// ...
// 准备window动画,进入,移除动画等
if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
mWindowManager.prepareAppTransition(TRANSIT_NONE, keepCurTransition);
mNoAnimActivities.add(r);
} else {
mWindowManager.prepareAppTransition(newTask
? r.mLaunchTaskBehind
? TRANSIT_TASK_OPEN_BEHIND
: TRANSIT_TASK_OPEN
: TRANSIT_ACTIVITY_OPEN, keepCurTransition);
mNoAnimActivities.remove(r);
}
addConfigOverride(r, task);
boolean doShow = true;
if (newTask) {
if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
resetTaskIfNeededLocked(r, r);
doShow = topRunningNonDelayedActivityLocked(null) == r;
}
} else if (options != null && options.getAnimationType()
== ActivityOptions.ANIM_SCENE_TRANSITION) {
doShow = false;
}
if (r.mLaunchTaskBehind) {
mWindowManager.setAppVisibility(r.appToken, true);
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
} else if (SHOW_APP_STARTING_PREVIEW && doShow) {
// ...
// 展示 startingWindow
r.showStartingWindow(prev, showStartingIcon);
}
} else {
// ...
}
- 将该 Activity 所对应的 AMS 和 WMS 的 task,分别移至栈顶;
- 从 mTaskHistory 获取该 Activity 所属的 task;
- 当 Activity 所属进程没有启动,则需要显示启动窗口,即 showStartingIcon=true;
- 创建 AppWindowToken,并添加到 WMS 的 mTokenMap;
- 设置启动窗口 (StartingWindow),r 为 ActivityRecord 类型;
- 恢复栈顶部的 Activity。
- 1.2 ActivityRecord.showStartingWindow()
void showStartingWindow(ActivityRecord prev, boolean createIfNeeded) {
final CompatibilityInfo compatInfo =
service.compatibilityInfoForPackageLocked(info.applicationInfo);
// 最终会调用 WindowManagerService 的 setAppStartingWindow()
final boolean shown = service.mWindowManager.setAppStartingWindow(
appToken, packageName, theme, compatInfo, nonLocalizedLabel, labelRes, icon,
logo, windowFlags, prev != null ? prev.appToken : null, createIfNeeded);
if (shown) {
mStartingWindowState = STARTING_WINDOW_SHOWN;
}
}
- 1.3 WindowManagerService.setAppStartingWindow()
@Override
public boolean setAppStartingWindow(IBinder token, String pkg,
int theme, CompatibilityInfo compatInfo,
CharSequence nonLocalizedLabel, int labelRes, int icon, int logo,
int windowFlags, IBinder transferFrom, boolean createIfNeeded) {
synchronized(mWindowMap) {
// 从mTokenMap中查找相应的Token(appWindowToken)
AppWindowToken wtoken = findAppWindowToken(token);
if (wtoken == null) {
// 当app的token为空,则直接返回
return false;
}
if (!okToDisplay()) {
// 当屏幕处于冻结状态,则直接返回
return false;
}
if (wtoken.startingData != null) {
// 当启动数据不为空,则无需再次设置
return false;
}
if (transferStartingWindow(transferFrom, wtoken)) {
return true;
}
if (!createIfNeeded) {
return false;
}
// 创建启动数据,并发送消息到"android.display"线程来处理ADD_STARTING消息
wtoken.startingData = new StartingData(pkg, theme, compatInfo, nonLocalizedLabel,
labelRes, icon, logo, windowFlags);
// 加入消息队列的头部,需要立马执行
Message m = mH.obtainMessage(H.ADD_STARTING, wtoken);
mH.sendMessageAtFrontOfQueue(m);
}
return true;
}
(1) 当上一个显示 Activity 的 ttoken 不为空的情况,有两种 case:
- 当 ttoken 的 startingWindow 和 startingView 都不为空,则将该启动窗口信息转移到新的 token,然后调用performLayoutAndPlaceSurfacesLocked() 进行布局和刷新 UI,则完成并返回;
- 当 ttoken 的 startingData 不为空 (上一个 activity 启动窗口准备好,但没有完成),则借用该 ttoken 启动数据,并发送消息到 "android.display" 线程来处理 ADD_STARTING 消息,则完成并返回。
(2) 当不存在启动窗口,且发起者不希望创建,则直接返回。
(3) 当主题 theme 不为空,主题满足以下任一情况,则都会直接返回:
- 该主题实体为空;
- 半透明;
- 浮动窗口;
- 需要显示壁纸。
最后,创建启动数据,并发送消息到 "android.display" 线程来处理 ADD_STARTING 消息。
- 1.4 WindowManagerService.H.handleMessage()
final class H extends Handler {
@Override
public void handleMessage(Message msg) {
case ADD_STARTING: {
final AppWindowToken wtoken = (AppWindowToken)msg.obj;
final StartingData sd = wtoken.startingData;
if (sd == null) {
// 动画被取消则返回
return;
}
View view = null;
try {
final Configuration overrideConfig = wtoken != null && wtoken.mTask != null
? wtoken.mTask.mOverrideConfig : null;
view = mPolicy.addStartingWindow(wtoken.token, sd.pkg, sd.theme,
sd.compatInfo, sd.nonLocalizedLabel, sd.labelRes, sd.icon, sd.logo,
sd.windowFlags, overrideConfig);
} catch (Exception e) {
Slog.w(TAG_WM, "Exception when adding starting window", e);
}
if (view != null) {
boolean abort = false;
synchronized(mWindowMap) {
// 当创建已被移除或者转移给其他activity,则需要移除它
if (wtoken.removed || wtoken.startingData == null) {
if (wtoken.startingWindow != null) {
wtoken.startingWindow = null;
wtoken.startingData = null;
abort = true;
}
} else {
wtoken.startingView = view;
}
}
if (abort) {
try {
// 此处mPolicy为PhoneWindowManager
mPolicy.removeStartingWindow(wtoken.token, view);
} catch (Exception e) {
}
}
}
} break;
}
}
关键一句在于 mPolicy.addStartingWindow(),mPolicy 在这边的实现为 PhoneWindowManager。
- 1.5 PhoneWindowManager.addStartingWindow()
@Override
public View addStartingWindow(IBinder appToken, String packageName, int theme,
CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
int icon, int logo, int windowFlags, Configuration overrideConfig) {
if (!SHOW_STARTING_ANIMATIONS) {
return null;
}
if (packageName == null) {
return null;
}
WindowManager wm = null;
View view = null;
try {
Context context = mContext;
// 设置主题
if (theme != context.getThemeResId() || labelRes != 0) {
try {
context = context.createPackageContext(packageName, 0);
context.setTheme(theme);
} catch (PackageManager.NameNotFoundException e) {
// Ignore
}
}
//...
// 创建 PhoneWindow
final PhoneWindow win = new PhoneWindow(context);
win.setIsStartingWindow(true);
CharSequence label = context.getResources().getText(labelRes, null);
// 设置窗体标题
if (label != null) {
win.setTitle(label, true);
} else {
win.setTitle(nonLocalizedLabel, false);
}
// 设置窗口类型为启动窗口类型
win.setType(
WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
synchronized (mWindowManagerFuncs.getWindowManagerLock()) {
if (mKeyguardHidden) {
windowFlags |= FLAG_SHOW_WHEN_LOCKED;
}
}
// 设置不可触摸和聚焦
win.setFlags(
windowFlags|
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE|
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
windowFlags|
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE|
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
win.setDefaultIcon(icon);
win.setDefaultLogo(logo);
win.setLayout(WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT);
final WindowManager.LayoutParams params = win.getAttributes();
params.token = appToken;
params.packageName = packageName;
params.windowAnimations = win.getWindowStyle().getResourceId(
com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
params.privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED;
params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
if (!compatInfo.supportsScreen()) {
params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
}
params.setTitle("Starting " + packageName);
// 获取WindowManager对象
wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
// 获取decorView
view = win.getDecorView();
// WindowManager添加View,在PhoneWindowManager中
wm.addView(view, params);
// 当窗口成功添加到WMS,则返回该对象
return view.getParent() != null ? view : null;
} catch (WindowManager.BadTokenException e) {
}
//...
return null;
}
这边就添加了 startingWindow了,具体逻辑看注释,最终调用 WindowManager.addView()。
- 1.6 结束 startingWindow
在 WindowManagerService 中:
void scheduleRemoveStartingWindowLocked(AppWindowToken wtoken) {
if (wtoken == null) {
return;
}
if (mH.hasMessages(H.REMOVE_STARTING, wtoken)) {
return;
}
if (wtoken.startingWindow == null) {
if (wtoken.startingData != null) {
wtoken.startingData = null;
}
return;
}
Message m = mH.obtainMessage(H.REMOVE_STARTING, wtoken);
mH.sendMessage(m);
}
然后 H 类收到消息:
case REMOVE_STARTING: {
final AppWindowToken wtoken = (AppWindowToken)msg.obj;
IBinder token = null;
View view = null;
synchronized (mWindowMap) {
if (wtoken.startingWindow != null) {
view = wtoken.startingView;
token = wtoken.token;
wtoken.startingData = null;
wtoken.startingView = null;
wtoken.startingWindow = null;
wtoken.startingDisplayed = false;
}
}
if (view != null) {
try {
mPolicy.removeStartingWindow(token, view);
} catch (Exception e) {
}
}
} break;
会调用 PhoneWindowManager.removeStartingWindow()。
@Override
public void removeStartingWindow(IBinder appToken, View window) {
if (window != null) {
WindowManager wm =(WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
wm.removeView(window);
}
}
最后调用 WindowManager.removeView()。
- 1.7 总结
startingWindow 的整个启动和结束过程,主要发送在 "android.display" 这个 Looper 线程,其中启动和结束最终分别调用了
WindowManagerGlobal 对象的 addView() 和 removeView() 操作,期间还有动画调度操作。
2. WindowManager 的 addView() 和 RemoveView()
具体的流程可以参考 Framework篇 - 一文搞懂 Activity、View、Window、ViewRootImpl。这边来介绍下 WindowManager 的 addView() 和 removeView() 的过程,实现在 WindowManagerImpl 中。
- 2.1 WindowManager.addView()
// WindowManager添加View
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
// 调用WindowManagerGlobal.addView
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
- 2.2 WindowManagerGlobal.addView()
// 添加View
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
// view不能为空
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
// display不能为空
if (display == null) {
throw new IllegalArgumentException("display must not be null");
}
// LayoutParams必须为WindowManager.LayoutParams类型
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
final Context context = view.getContext();
if (context != null
&& (context.getApplicationInfo().flags
& ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
}
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
// ...
// 每次调用global.addView()都会创建一个ViewRootImpl,它是decorView与WMS沟通的桥梁
root = new ViewRootImpl(view.getContext(), display);
// 设置LayoutParams
view.setLayoutParams(wparams);
// 加到mViews, mRoots, mParams集合中
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
try {
// ViewRootImpl设置View
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}
里面会创建 ViewRootImpl,接下来看看 ViewRootImpl。
- 2.3 ViewRootImpl
public ViewRootImpl(Context context, Display display) {
mContext = context;
// 获取IWindowSession的代理类
mWindowSession = WindowManagerGlobal.getWindowSession();
mDisplay = display;
mThread = Thread.currentThread(); //主线程
mWindow = new W(this);
mChoreographer = Choreographer.getInstance();
...
}
初始化变量:
- mWindowSession:WMS 端的 Session 的代理对象;
- mWindow: 继承于 IWindow.Stub 的 W 对象;
- mChoreographer:绘制相关的对象。
- 2.4 WindowManagerGlobal.getWindowSession()
// ViewRootImpl构造器中调用 -> 获取IWindowSession的代理类
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
// 获取IMS的代理类
InputMethodManager imm = InputMethodManager.getInstance();
// 获取WMS的代理类
IWindowManager windowManager = getWindowManagerService();
// 经过Binder调用,最终调用WMS
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
通过 Binder 调用进入 system_server 进程,执行 openSession() 的操作。
- 2.5 WindowManagerService.openSession()
// 打开Session
@Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
// 构建Session
Session session = new Session(this, callback, client, inputContext);
return session;
}
再次经过 Binder 将数据写回 app 进程,则获取的便是 Session 的代理对象,接着来看看上面的 root.setView() 的实现。
- 2.6 ViewRootImpl.setView()
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
// ...
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
// 通过Binder调用,进入system进程的Session
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
} catch (RemoteException e) {
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
mInputChannel = null;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
throw new RuntimeException("Adding window failed", e);
} finally {
if (restore) {
attrs.restore();
}
}
// ...
}
}
}
通过 Binder 调用,进入 system_server 进程的 Session 对象。
- 2.7 Session.addToDisplay()
final class Session extends IWindowSession.Stub
implements IBinder.DeathRecipient {
@Override
public int add(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, Rect outContentInsets, Rect outStableInsets,
InputChannel outInputChannel) {
return addToDisplay(window, seq, attrs, viewVisibility, Display.DEFAULT_DISPLAY,
outContentInsets, outStableInsets, null /* outOutsets */, outInputChannel);
}
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel) {
// 这边会进入WindowManagerService,进行addWindow操作
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
}
最终在 Session 里面调用 WindowManagerService 的 addWindow() 方法。这边就不往下看了,感兴趣的同学可以去看看源码。WindowManager.removeView() 逻辑和 addView() 相似,就不再赘述。
3. 总结
Activity 的启动过程同时贯穿着 AMS/WMS 相关信息的创建,本文从 Window 角度来看待整个过程:
第一阶段运行在 system_server 进程:
将 Window 相应 task 移至顶部,并创建 AppWindowToken 对象,添加到 WMS.mTokenMap 记录该信息;
发送消息到 "android.display" 线程来处理 ADD_STARTING 启动窗口的消息。
第二阶段运行在目标进程的主线程:(当然在该阶段有些方法会 Binder call 到 system_server 进程)
创建获取 AMS 的 Binder 代理;
执行 performLaunchActivity() 过程,创建如下对象:
(1) 创建 LoadedApk 对象;
(2) 创建 ComponentName 对象;
(3) 创建目标 Activity 对象;
(4) 创建 Application 对象;
(5) 创建完 Activity,执行 attach 操作,初始化成员变量:
- mWindow:数据类型为 PhoneWindow,继承于 Window 对象;
- mWindowManager:数据类型为 WindowManagerImpl,实现 WindowManager 接口;
- mToken:远程 ActivityRecord 的 appToken 的代理端。
执行performResumeActivity() 过程:
(1) 回调 onResume() 方法;
(2) 执行 addView 过程;
(3) 创建 ViewRootImpl 对象;
(4) 创建 WMS 端的 Session 的代理对象;
(5) 创建继承于 IWindow.Stub 的 ViewRootImpl.W 对象;
(6) 执行 setView() 添加视图到 WMS;
(7) 在 WMS 中创建 WindowState 对象;
(8) updateFocusedWindowLocked() 来更新聚焦窗口情况。