APP窗口基础
核心类
-
Activity负责生命周期和事件处理以及具体的业务逻辑;
-
Window控制视图,一个Activity至少包含一个Window,如果Activity没有Window,那就相当于Service。
-
Window是一个抽象类,具体的实现类为PhoneWindow,它对View进行管理;从用户的角度来讲,它是一个“交互界面”;从SurfaceFlinger的角度来看,它是一个Layer,承载着和窗口有关的数据和属性;从WMS角度来看,它是一个WindowState,用于管理和窗口有关的状态。
-
WindowManager是一个接口类,继承自接口ViewManager它是用来管理Window的,它的实现类为WindowManagerImpl,然后WindowManagerImpl的功能具体实现都委托给了WindowManagerGlobal,WindowManagerGlobal是进程单例的,它管理所有的ViewRootImpl,实际执行addView。
-
DecorView是FrameLayout的子类,是窗口View树的根节点,包含了ActionBar和ContentParent,在onCreate时
setContentView()→installDecor()时创建。 -
ViewRootImpl是View系统的核心类,核心功能如下
- 通过WindowSession和IWindow和WMS通信,负责窗口的添加和删除、处理WMS下发的Resize/FocusChanged等信息,
- 负责View的三大流程Measure/Layout/Draw,并负责管理应用的Surface和创建BBQ,和RenderThread一起完成应用的绘制;
- 也是输入事件的中转站,持有InputChannel,接收Input下发的事件并转发给Activity和View处理。
-
每个Window都属于一个具体的类型,例如应用窗口、子窗口和系统窗口;Activity对应的Window是应用窗口;PopupWindow,OptionMenu是常用的子窗口;像状态栏和导航栏就是系统窗口。
窗口添加流程
APP构建窗口
从handleLaunchActivity到generateDecor
总结:Activity的创建和PhoneWindow的窗口都是在Activity Launch的时候(即Create、Start的时候)
handleLaunchActivity
activity启动的核心函数
- 调用Instrumentation.newActivity方法, 利用反射创建activity
- 执行attach函数,为activity创建PhoneWindow(绑定Window)
- 执行onCreate生命周期回调,并设置activity的状态为on_create,会执行到自定义Activity的onCreate()方法。
//android.app.ActivityThread#performLaunchActivity
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//1. 利用反射创建activity
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
} catch (Exception e) {
....
}
//2. 创建Application
try {
Application app = r.packageInfo.makeApplication( false , mInstrumentation);
// 3. 执行attach函数,创建Window
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken, r.shareableActivityToken);
// 4. 执行生命周期的回调, wm_on_create_called log打印的地方
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
}
//5. 将activity的状态设置为on_create
r.setState(ON_CREATE);
}
return activity;
}
Activity#attach
- 新建一个PhoneWindow作为当前activity的Window
- 设置回调,设置主线程,设置WindowManager
//android.app.Activity#attach
final void attach(Context context, ActivityThread aThread,...) {
//1. 新建一个PhoneWindow作为当前activity的Window
mWindow = new PhoneWindow(this, window, activityConfigCallback);
//2. 设置主线程
mUiThread = Thread.currentThread();
mMainThread = aThread;
mInstrumentation = instr;
//3. 为当前Window设置WindowManager
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
mWindowManager = mWindow.getWindowManager();
}
handleResumeActivity
窗口添加的系统侧是在activity resume的时候
- 获取当前window的DecorView 作为添加到WMS的View
- 获取当前window的属性,作为布局参数添加到WMS
- 调用WindowManagerImpl的addView方法将View和布局参数添加到WMS
//android.app.ActivityThread#handleResumeActivity
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
boolean isForward, String reason) {
// 执行生命周期的回调, wm_on_resume_called log打印的地方 设置ON_RESUME
if (!performResumeActivity(r, finalStateRequest, reason)) {
return;
}
final Activity a = r.activity;
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
//获取当前window的DecorView 作为添加到WMS的View
View decor = r.window.getDecorView();
ViewManager wm = a.getWindowManager();
//获取当前window的属性,作为布局参数添加到WMS
WindowManager.LayoutParams l = r.window.getAttributes();
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
//调用WindowManagerImpl的addView方法将View和布局参数添加到WMS
wm.addView(decor, l);
}
}
}
}
WindowManagerGlobal#addView
- 先判断是否存在父Window,如果存在则会将新创建的Window的参数和父Window的携带调整为一致
- 创建ViewRootImpl
- 记录view, 记录ViewRootImpl,记录布局参数
//android.view.WindowManagerGlobal#addView
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {
synchronized (mLock) {
// 创建ViewRootImpl, 并构建IWindowSession
if (windowlessSession == null) {
root = new ViewRootImpl(view.getContext(), display);
} else {
root = new ViewRootImpl(view.getContext(), display,
windowlessSession);
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
//如果是子Window即parentWindow != null,则会调整SubWindow的参数
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
}
view.setLayoutParams(wparams);
//记录view, 记录ViewRootImpl,记录布局参数
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
//将View, params, 和userId添加到WMS
root.setView(view, wparams, panelParentView, userId);
}
}
}
adjustLayoutParamsForSubWindow的主要逻辑,
android.view.Window#adjustLayoutParamsForSubWindow
void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {
CharSequence curTitle = wp.getTitle();
//如果是subWindow,则将token设置为decorView的token,即主Window的Token
if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
if (wp.token == null) {
View decor = peekDecorView();
if (decor != null) {
wp.token = decor.getWindowToken();
}
}
} else if (wp.type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW &&
wp.type <= WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) {
//处理系统窗口
if (curTitle == null || curTitle.length() == 0) {
final StringBuilder title = new StringBuilder(32);
title.append("Sys").append(wp.type);
if (mAppName != null) {
title.append(":").append(mAppName);
}
wp.setTitle(title);
}
} else {
//其他情况设置为Window的mAppToken
if (wp.token == null) {
wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
}
if ((curTitle == null || curTitle.length() == 0)
&& mAppName != null) {
wp.setTitle(mAppName);
}
}
if (wp.packageName == null) {
wp.packageName = mContext.getPackageName();
}
}
ViewRootImpl#setView
ViewRootImpl是一个Window中View树的根,因此与Activity、Window、WindowState等一一对应。 通过Session和Window对象,实现Client端和WMS端的交互。
主要负责:
- Client->WMS: Window的添加和删除:WindowSession#addToDisplay,WindowSession#remove
- WMS-> Client: 处理WMS下发的一些Winodw变化:resized, moved, windowFocusChanged
- 核心功能:View的三大流程,测量、布局和绘制:measure, layout, draw
- Input事件:输入事件的中转站,处理keyevent, motion event等
- 窗口关键信息管理:View.AttachInfo, 管理Surface
- mWindowSession.addToDisplayAsUser 通过mWindowSession将窗口,布局参数等其他属性添加进WMS,其中mWindowSession类型是IWindowSession,在构造方法赋值,通过WindowManagerGlobal.getWindowSession()获取。
- 在mWindowLayout.computeFrames处计算窗口的大小
- setFrame(mTmpFrames.frame); 更新窗口的大小
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
synchronized (this) {
if (mView == null) {
mView = view;
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
//在将窗口添加到系统侧之前,先进行一次布局,这样可以保证在收到系统事件前做了relayout
requestLayout();
try {
//将当前窗口添加到系统侧
res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId,
mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,
mTempControls, attachedFrame, compatScale);
}
//计算当前窗口的大小
mWindowLayout.computeFrames(mWindowAttributes, state,
displayCutoutSafe, winConfig.getBounds(), winConfig.getWindowingMode(),
UNSPECIFIED_LENGTH, UNSPECIFIED_LENGTH,
mInsetsController.getRequestedVisibilities(), 1f /* compactScale */,
mTmpFrames);
//更新当前窗口的大小
setFrame(mTmpFrames.frame);
}
}
}
WMS添加窗口
WindowToken创建和挂载
窗口添加时,核心问题是WindowToken挂到层级树的哪个节点。入口在DisplayContent.addWindowToken():
// DisplayContent.java
final DisplayArea.Tokens da = findAreaForToken(token).asTokens();
da.addChild(token);
findAreaForWindowType()根据窗口类型做三路分派:
// DisplayContent.java
if (windowType >= FIRST_APPLICATION_WINDOW && windowType <= LAST_APPLICATION_WINDOW) {
//Type是application的,则返回TaskDisplayArea
return mDisplayAreaPolicy.getTaskDisplayArea(options); // → TaskDisplayArea
}
if (windowType == TYPE_INPUT_METHOD || windowType == TYPE_INPUT_METHOD_DIALOG) {
return mImeContainer; // → ImeContainer
}
return mDisplayAreaPolicy.findAreaForWindowType(...); // → 系统窗口对应的DisplayArea.Tokens
对于系统窗口,最终调用RootDisplayArea.findAreaForWindowTypeInLayer():
// RootDisplayArea.java
int windowLayerFromType = mWmService.mPolicy.getWindowLayerFromTypeLw(
windowType, ownerCanManageAppTokens, roundedCornerOverlay);
return mAreaForLayer[windowLayerFromType]; // 通过数组直接索引
WindowToken的层级
这些层级数字对应mAreaForLayer[]数组的索引,每个索引指向一个预构建的DisplayArea.Tokens容器。
WindowManagerPolicy.getWindowLayerFromTypeLw()将窗口type映射为数字层级(1-36):
| 层级 | 窗口类型 | 说明 |
|---|---|---|
| 1 | TYPE_WALLPAPER | 壁纸 |
| 2 | APPLICATION_LAYER | 应用窗口(特殊,走TaskDisplayArea) |
| 3 | TYPE_PHONE / TYPE_PRESENTATION | 电话/演示窗口 |
| 7/27 | TYPE_TOAST | 普通Toast=7,系统Toast=27 |
| 9/12 | TYPE_SYSTEM_ALERT | 普通=9,系统=12 |
| 11 | TYPE_APPLICATION_OVERLAY | 应用悬浮窗 |
| 13 | TYPE_INPUT_METHOD | 输入法 |
| 15 | TYPE_STATUS_BAR | 状态栏 |
| 17 | TYPE_NOTIFICATION_SHADE | 通知面板 |
| 24 | TYPE_NAVIGATION_BAR | 导航栏 |
| 26 | TYPE_SCREENSHOT | 截屏 |
| 31 | TYPE_ACCESSIBILITY_OVERLAY | 无障碍覆盖 |
| 35 | TYPE_POINTER | 指针 |
| 36 | 圆角覆盖层 | 最高层级 |
RootDisplayArea
├─ DisplayArea.Tokens [layer=1] ← 壁纸
├─ ...
├─ TaskDisplayArea [layer=2] ← 应用窗口
│ └─ Task → ActivityRecord → WindowState
├─ ...
├─ ImeContainer [layer=13-14] ← 输入法
├─ DisplayArea.Tokens [layer=15] ← 状态栏
├─ DisplayArea.Tokens [layer=24] ← 导航栏
├─ ...
└─ DisplayArea.Tokens [layer=35] ← 指针
DisplayAreaPolicyBuilder在Display创建时构建这棵树,每个层级对应一个DisplayArea.Tokens叶节点。
WindowState创建和添加
创建WidowState,SurfaceSession,并将WindowState添加到ActivityRecord的过程
- 创建WindowState,并创建WindowStateAnimator管理Surface的状态,mDrawState默认为NO_SURFACE = 0
- 建立与SF的链接,并创建Layer,后续流程见Surface创建流程
- 添加到mWindowMap, 由WMS统一管理
- 添加到WindowToken中,把属于同一WindowToken的Window组织在一起
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
int displayId, ...) {
//获取 displayContent
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
//如果有父Window则用父Window的Token,如果是子Window则用子Window的token。
final boolean hasParent = parentWindow != null;
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
//创建 WindowState 并创建WindowStateAnimator
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
// 创建SurfaceSession,建立与SF的链接
win.attach();
//添加到mWindowMap, 由WMS统一管理
mWindowMap.put(client.asBinder(), win);
//添加到WindowToken中,把属于同一WindowToken的Window组织在一起
win.mToken.addWindow(win);
}
将WindowState对象添加到WindowToken中,
void addWindow(final WindowState win) {
ProtoLog.d(WM_DEBUG_FOCUS,
"addWindow: win=%s Callers=%s", win, Debug.getCallers(5));
//子窗口有父窗口管理,不需要WindowToken管理
if (win.isChildWindow()) {
// Child windows are added to their parent windows.
return;
}
//如果没有SurfaceControl,则创建SurfaceControl, 什么情况下会走这里?
if (mSurfaceControl == null) {
createSurfaceControl(true /* force */);
// Layers could have been assigned before the surface was created, update them again
reassignLayer(getSyncTransaction());
}
//添加到mChildren中,并设置mParent属性
if (!mChildren.contains(win)) {
ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Adding %s to %s", win, this);
addChild(win, mWindowComparator);
mWmService.mWindowsChanged = true;
}
}
WindowState的Z-Order
WMS添加窗口小结
宏观定位:
窗口type → getWindowLayerFromTypeLw() → 层级数字 → mAreaForLayer[] → 对应的DisplayArea.Tokens容器
决定窗口在层级树中的大致位置
微观排序:
WindowState通过mBaseLayer比较器在WindowToken内部有序插入 → assignChildLayers()按children顺序递增分配最终Z-order值给SurfaceFlinger
决定窗口的精确Z-order
WindowToken的创建与传递
在ActivityRecord的构造方法中会new一个Token对象,同时调用super方法到WindowToken中,然后token通过弱引用持有ActivityRecord。
可以看到Token是一个Binder(这样方便跨进程传输);
在WindowToken的构造函数中,会将token和WindowToken添加到DisplayContent中的mTokenMap。
//com.android.server.wm.ActivityRecord#ActivityRecord
private ActivityRecord(.....) {
super (_service.mWindowManager, new Token (), TYPE_APPLICATION , true ,
null /* displayContent */ , false /* ownerCanManageAppTokens */ );
mAtmService = _service;
((Token) token).mActivityRef = new WeakReference <>( this );
info = aInfo;
mUserId = UserHandle.getUserId(info.applicationInfo.uid);
packageName = info.applicationInfo.packageName;
intent = _intent;
}
//com.android.server.wm.ActivityRecord.Token
private static class Token extends Binder {
@NonNull WeakReference<ActivityRecord> mActivityRef;
@Override
public String toString() {
return "Token{" + Integer.toHexString(System.identityHashCode(this)) + " "
+ mActivityRef.get() + "}";
}
}
//com.android.server.wm.WindowToken#WindowToken
protected WindowToken(WindowManagerService service, IBinder _token, int type,
boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens,
boolean roundedCornerOverlay, boolean fromClientToken, @Nullable Bundle options) {
super(service);
token = _token;
if (dc != null) {
dc.addWindowToken(token, this);
}
}
//com.android.server.wm.DisplayContent#addWindowToken
void addWindowToken(IBinder binder, WindowToken token) {
final DisplayContent dc = mWmService.mRoot.getWindowTokenDisplay(token);
mTokenMap.put(binder, token);
}
WindowToken的传递到Client
- 在realStartActivityLocked中发送LaunchActivityItem时,会将token这个Binder封装到ClientTransaction中。
com.android.server.wm.ActivityTaskSupervisor#realStartActivityLocked
boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
boolean andResume, boolean checkConfig) throws RemoteException {
......
// Create activity launch transaction.
final ClientTransaction clientTransaction = ClientTransaction.obtain(
proc.getThread(), r.token );
final boolean isTransitionForward = r.isTransitionForward();
final IBinder fragmentToken = r.getTaskFragment().getFragmentToken();
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
System.identityHashCode(r), r.info,
// TODO: Have this take the merged configuration instead of separate global
// and override configs.
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(), r.compat,
r.getFilteredReferrer(r.launchedFromPackage), task.voiceInteractor,
proc.getReportedProcState(), r.getSavedState(), r.getPersistentSavedState(),
results, newIntents, r.takeOptions(), isTransitionForward,
proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController,
r.shareableActivityToken, r.getLaunchedFromBubble(), fragmentToken));
......
}
2. 在执行生命周期回调时,会从ClientTransaction中取出token这个Binder,然后执行LaunchActivityItem的execute方法;其中会构造ActivityClientRecord对象,会将此token保存到ActivityClientRecord中的token属性中。
这样就完成了token从SystemServer到Client端的传递。
//android.app.servertransaction.TransactionExecutor#executeCallbacks
public void executeCallbacks(ClientTransaction transaction) {
final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
if (callbacks == null || callbacks.isEmpty()) {
// No callbacks to execute, return early.
return;
}
if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callbacks in transaction");
final IBinder token = transaction.getActivityToken();
ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
final int size = callbacks.size();
for (int i = 0; i < size; ++i) {
final ClientTransactionItem item = callbacks.get(i);
//此时的item是LaunchActivityItem
item.execute(mTransactionHandler, token, mPendingActions);
}
}
//android.app.servertransaction.LaunchActivityItem#execute
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
ActivityClientRecord r = new ActivityClientRecord (token, ....);
client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
//android.app.ActivityThread.ActivityClientRecord#ActivityClientRecord
public ActivityClientRecord(IBinder token, ....) {
this .token = token;
....
init();
}
WindowToken的传递到Window
- 在上一步创建ActivityClientRecord后会将执行handleLaunchActivity,进一步执行performLaunchActivity,这里会创建Activity以及执行android.app.Activity#attach方法去创建Window。接下来看下这个过程中token的传递逻辑。
//android.app.ActivityThread#performLaunchActivity
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
....
Activity activity = null;
java.lang.ClassLoader cl = appContext.getClassLoader();
//通过mInstrumentation反射创建Activity
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
//创建Application
Application app = r.packageInfo.makeApplicationInner(false, mInstrumentation);
//执行Activity#attach去创建Window
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.activityConfigCallback,
r.assistToken, r.shareableActivityToken);
//将Activity赋值给ActivityClientRecord中的activity属性
r.activity = activity;
//执行onCreate生命周期
mInstrumentation.callActivityOnCreate(activity, r.state);
r.setState(ON_CREATE);
}
2. 在attach方法中首先创建PhoneWindow作为Activity的Window,然后将ActivityClientRecord的token赋值给Activity的mToken属性;
然后给Window设置WindowManager,同时将mToken设置为WindowManager的mAppToken。
这样就完成了token传递给Window。
//android.app.Activity#attach
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, ...) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
//创建PhoneWindow作为Activity的Window
mWindow = new PhoneWindow ( this , window, activityConfigCallback);
mUiThread = Thread.currentThread();
mMainThread = aThread;
mInstrumentation = instr;
//将token赋值给Activity的mToken属性
mToken = token;
mApplication = application;
mIntent = intent;
mReferrer = referrer;
mComponent = intent.getComponent();
mActivityInfo = info;
mTitle = title;
mParent = parent;
mEmbeddedID = id;
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;
}
//android.view.Window#setWindowManager
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated;
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}