在上一篇文章中分析了Android中WMS窗口显示流程中的创建窗口,现在从源码的角度分析下添加窗口的逻辑处理。
应用进程启动后,会执行两个事务,分别触发到 Activity 的 onCreate 和 onResume 生命周期。
在onCreate事务处理中,由 LaunchActivityItem 事务触发 handleLaunchActivity;创建 Activity;创建 Window,而 Window 是抽象类,PhoneWindow 唯一实现类;执行到 onCreate 生命周期。在上篇文章中已经分析过。
在onResume事务处理中,由 ResumeActivityItem 事务触发 handleResumeActivity;触发 onResume 的执行流程;执行 WindowManagerImpl::addView 实际上流程是交给了 WindowManagerGlobal::addView 处理;创建核心类 ViewRootImpl;执行关键函数 ViewRootImpl::setView ,这里会触发 WMS 三大流程。
本文以android 13代码为例进行分析。
1,在frameworks/base/core/java/android/app/ActivityThread.java中
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,boolean isForward, String reason) {
if (!performResumeActivity(r, finalStateRequest, reason)) { //调用 performResumeActivity,触发onResume
return;
}
final Activity a = r.activity;
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow(); // 获取PhoneWindow,将window设置到ActivityRecord中
View decor = r.window.getDecorView(); //获取DecorView
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager(); //实际上是WindowManagerImpl
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; //设置Activity的windowType,注意这个type,才是应用的窗口类型
ViewRootImpl impl = decor.getViewRootImpl();
a.mWindowAdded = true;
wm.addView(decor, l); //调用ViewManager的addView函数,会进一步地走到创建ViewRootImpl,走到WindowManagerGlobal类的addView函数。
}
}
上面的函数调用 performResumeActivity 函数。
public boolean performResumeActivity(ActivityClientRecord r, boolean finalStateRequest,String reason) {
...
r.activity.performResume(r.startsNotResumed, reason); // 调用Activity的performResume()方法,传入是否从非恢复状态开始以及恢复的原因。
r.state = null;
r.persistentState = null;
r.setState(ON_RESUME); // 将Activity的状态更新为ON_RESUME,并清除之前可能存在的状态信息。
return true;
}
对于上面的代码,performResumeActivity 会触发 onResume 。执行顺序在 handleResumeActivity 函数中调用 addView 之前,说明 onResume 只是 Activity 可见,而不是 UI 可见。通过 WindowManagerImpl::addView 触发后续逻辑。
2,在frameworks/base/core/java/android/app/Activity.java中
private Instrumentation mInstrumentation; // mInstrumentation 变量在执行 attach 函数时由传入的参数赋值。
final void performResume(boolean followedByPause, String reason) {
mInstrumentation.callActivityOnResume(this); // 调用Activity的onResume方法
}
在执行了 performResumeActivity 函数后,后执行 ViewManager 类的 addView 函数。 ViewManager 是一个接口,即 public interface ViewManager,定义有addView、updateViewLayout、removeView等三个函数。 WindowManager 也是一个接口,继承自 ViewManager ,即 public interface WindowManager extends ViewManager 。 WindowManagerImpl 是 WindowManager 的实现类。
3,在frameworks/base/core/java/android/view/WindowManagerImpl.java中
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyTokens(params);
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
mContext.getUserId());
}
}
上面的代码调用 WindowManagerGlobal 类的 addView 函数。
4,在frameworks/base/core/java/android/view/WindowManagerGlobal.java中
public final class WindowManagerGlobal {
private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow, int userId) {
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams); // 调整参数,比如设置 token ,title,和硬件加速的标志位这3个比较重要的参数
}
ViewRootImpl root; //创建ViewRootImpl对象
IWindowSession windowlessSession = null;
if (windowlessSession == null) { // 对于应用来说 windowlessSession 是为null的,在ViewRootImpl的构造函数中通过WindowManagerGlobal.getWindowSession()作为windowSession
root = new ViewRootImpl(view.getContext(), display); //创建ViewRootImpl类对象
} else {
root = new ViewRootImpl(view.getContext(), display,windowlessSession);
}
view.setLayoutParams(wparams); //设置参数到 decorView
mViews.add(view); // WindowManagerGlobal 是进程单例维护了这个应用中多有的 DecorView
mRoots.add(root);
mParams.add(wparams);
try {
root.setView(view, wparams, panelParentView, userId); //与WMS通信触发窗口的显示逻辑,传递的 view 就是 PhoneWindow 持有的 DecorView
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
上面的代码调用 adjustLayoutParamsForSubWindow 进行设置 token 等操作(对于token的设置参考本文步骤15),并创建了 ViewRootImpl 对象以及 IWindowSession对象,并进一步调用 setView 函数。 下面分析下 setView 函数。
5,在frameworks/base/core/java/android/view/ViewRootImpl.java中
public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,AttachedSurfaceControl {
final IWindowSession mWindowSession;//与WMS通信的binder, 其初始化是在ViewRootImpl的构造函数中由传入的参数进行赋值
final Choreographer mChoreographer;
final W mWindow;
View mView; //对应的DecorView
public final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();
public final Surface mSurface = new Surface(); // 应用端这个View树的 Surface,这个mSurface是专门提供给docerview上面的canvas. 真正的 Suface 创建是在 system_service 端触发
private final SurfaceControl mSurfaceControl = new SurfaceControl(); // 对应的SurfaceControl
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,int userId) {
synchronized (this) {
if (mView == null) { // mView为null,表示这是第一次设置视图
mView = view; // 将DecorView赋值给 mView
requestLayout(); // 在添加到窗口管理器之前请求首次布局,确保在接收其他系统事件之前完成重新布局。relayoutWindow和finishDrawingWindow都在这里触发
InputChannel inputChannel = null; // 用于窗口接收input
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { // 如果需要输入通道(没有设置INPUT_FEATURE_NO_INPUT_CHANNEL标志),则创建一个新的InputChannel。
inputChannel = new InputChannel();
}
// 通过binder通信,调用wms的addWindow方法,addToDisplayAsUser的第一个参数mWindow在ViewRootImpl的构造函数中初始化即 mWindow = new W(this);
// 调用的 Session::addToDisplayAsUser 最终执行的是 WindowManagerService:: addWindow
res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), userId,
mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,
mTempControls); // 尝试将窗口添加到指定的显示和用户中。这一步涉及到多个参数,如窗口、窗口属性、可见性、显示ID、用户ID等
view.assignParent(this); // 将视图(view)的父级设置为当前窗口(this)。DecorView::getParent 返回的是 ViewRootImpl 的原因
}
}
}
}
上面的 setView 方法,执行了 requestLayout 方法,并进一步执行 scheduleTraversals 函数代码的目的是确保UI遍历(包括测量、布局和绘制)被调度执行。通过 Choreographer 来协调这个过程,并利用同步屏障来确保在遍历执行期间,消息队列中的其他同步消息不会干扰到遍历过程。
在执行 requestLayout 后,又执行了 addToDisplayAsUser 函数,在代码顺序上是先执行 requestLayout 再执行 addToDisplayAsUser,就是因为 requestLayout 方法内部需要等待 Vsync 的到来,并且还是异步执行 Runable ,所以 addToDisplayAsUser 触发的 addWindow 流程是先于 relayoutWindow 流程执行的。
setView方法是一个核心方法,分别调用 requestLayout 和 addToDisplayAsUser 函数,其触发的调用链如下:
ViewRootImpl::setView
ViewRootImpl::requestLayout
ViewRootImpl::scheduleTraversals
ViewRootImpl.TraversalRunnable::run -- Vsync相关--scheduleTraversals
ViewRootImpl::doTraversal
ViewRootImpl::performTraversals
ViewRootImpl::relayoutWindow
Session::relayout -- 第二步:relayoutWindow
ViewRootImpl::updateBlastSurfaceIfNeeded
Surface::transferFrom -- 应用端Surface赋值
ViewRootImpl::performMeasure -- View绘制三部曲 --Measure
ViewRootImpl::performLayout -- View绘制三部曲 --Layout
ViewRootImpl::createSyncIfNeeded
ViewRootImpl::reportDrawFinished
Session::finishDrawing -- 第三步:finishDrawingWindow
ViewRootImpl::performDraw -- View绘制三部曲 --Draw
SurfaceSyncGroup::markSyncReady -- 触发绘制完成回调
Session.addToDisplayAsUser
setView 的整体调用链如上所示,后面逐步分析。
下面首先看在 setView 中有一个 mWindowSession 对象,通过该 mWindowSession 变量调用 addToDisplayAsUser 函数。 mWindowSession 的赋值是在ViewRootImpl的构造函数中进行的,即mWindowSession = session;这个 session值是通过WindowManagerGlobal.getWindowSession()获取的,后面再详细分析。
接着看 requestLayout 函数。
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread(); //只有主线程才能更新UI
mLayoutRequested = true;
scheduleTraversals(); //调用scheduleTraversals函数
}
}
上面的代码调用 scheduleTraversals 函数。
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 通过消息循环(Looper)的队列(Queue)发布一个同步屏障(Sync Barrier)。这个屏障用于阻塞消息队列中同步消息的处理,直到屏障被移除。mTraversalBarrier变量保存了这个屏障的引用。
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// 调用Choreographer对象的postCallback方法。Choreographer是Android系统中用于协调动画、输入和绘制的一个类。
// 向Choreographer注册一个回调。CALLBACK_TRAVERSAL是回调的类型,表示这是一个遍历回调。
// mTraversalRunnable是一个实现了遍历逻辑的可运行对象(Runnable),当回调被执行时,它会运行。
mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); // 执行 mTraversalRunnable,当下一个 VSync-app 到来的时候,会执行 TraversalRunnable 这个 Runnable
notifyRendererOfFramePending(); // 通知渲染器有一帧即将被绘制。这通常是为了确保渲染器准备好处理新的帧。
pokeDrawLockIfNeeded(); // 根据需要戳一下绘制锁
}
}
上面的代码中执行 mTraversalRunnable,当下一个 VSync-app 到来的时候,会执行 TraversalRunnable 这个 Runnable。
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal(); //在Runnable运行时调用doTravelsal,并最终调用到 performTraversals 函数
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
// 通过消息循环(Looper)的队列(Queue)移除之前发布的同步屏障(Sync Barrier)。
// 这个屏障是在scheduleTraversals方法中发布的,用于阻塞消息队列中同步消息的处理,直到遍历任务开始执行。现在遍历任务即将开始,所以移除这个屏障以允许其他同步消息继续被处理。
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); //移除同步屏障,那么 mHandler 就可以正常处理后面的消息了
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals(); // 执行遍历任务。这个方法包含了测量、布局和绘制的实际逻辑
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
上面的代码调用 performTraversals 函数。
private final SurfaceSyncer mSurfaceSyncer = new SurfaceSyncer();
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView; // 获取根视图(mView)并存储在局部变量host中
// 调用relayoutWindow方法,传入参数、视图可见性和是否需要处理insets(内部空间),返回重新布局的结果。
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); //经过relayoutWindow 后就 View 就可以绘制了
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width,lp.privateFlags);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height,lp.privateFlags);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); // 调用performMeasure方法,根据新的测量规格(childWidthMeasureSpec和childHeightMeasureSpec)来测量视图的大小。
layoutRequested = true;
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
if (didLayout) { // 如果请求了布局(layoutRequested为true)且视图没有被停止(mStopped为false)或报告下一个绘制(mReportNextDraw为true),则执行布局(performLayout)
performLayout(lp, mWidth, mHeight); //调用performLayout函数
}
boolean cancelAndRedraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw();
if (!cancelAndRedraw) {
createSyncIfNeeded(); //设置了回调,等时机到了就会触发执行,而这个时机就是 View 绘制完成后,在 markSyncReady 触发,开始finish drawing流程
}
// 调用performDraw()尝试绘制,如果绘制失败且存在同步回调,则调用回调。
if (!performDraw() && mSyncBufferCallback != null) {
mSyncBufferCallback.onBufferReady(null);
}
if (!cancelAndRedraw) {
mReportNextDraw = false;
mSyncBufferCallback = null;
mSyncBuffer = false;
if (isInLocalSync()) {
mSurfaceSyncer.markSyncReady(mSyncId); //触发绘制完成回调,markSyncReady 方法,最终会触发 ViewRootImpl::createSyncIfNeeded 方法下的 ViewRootImpl::reportDrawFinished 来真正 finishDrawingWindow 流程。
mSyncId = UNSET_SYNC_ID;
}
}
}
上面的代码中在 performTraversals 函数中分别执行了relayoutWindow,performMeasure,performLayout,performDraw等函数。并执行createSyncIfNeeded函数执行完成绘制。
对于ViewRootImpl类的 setView 函数,上面对 requestLayout 做了基本分析,下面看下 addToDisplayAsUser 函数的执行,即 mWindowSession.addToDisplayAsUser(...)。
mWindowSession 是 IWindowSession 类型的对象,用于当前APP进程与WMS跨进程通信。
6, 在frameworks/base/core/java/android/view/IWindowSession.aidl中
interface IWindowSession {
int addToDisplayAsUser(...);
}
7, 在frameworks/base/services/core/java/com/android/server/wm/Session.java中
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
final WindowManagerService mService;
final IWindowSessionCallback mCallback;
public Session(WindowManagerService service, IWindowSessionCallback callback) {
mService = service;
mCallback = callback;
}
@Override
public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,int viewVisibility, int displayId, int userId, InsetsVisibilities requestedVisibilities,
InputChannel outInputChannel, InsetsState outInsetsState,InsetsSourceControl[] outActiveControls) {
return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
requestedVisibilities, outInputChannel, outInsetsState, outActiveControls); //返回WMS的addWindow函数
}
}
此时可以看到 mWindowSession 调用 addToDisplayAsUser 函数会执行到WMS的 addWindow 函数中,这样就与WMS有了联系。 那么就分析看下 mWindowSession 是怎样得到的,经过上面的分析已经知道 mWindowSession是在ViewRootImpl 类的构造函数中被赋值的,那么继续看下这个参数是怎么构造的。
8,在frameworks/base/core/java/android/view/WindowManagerGlobal.java中
public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow, int userId) {
ViewRootImpl root; //创建ViewRootImpl对象
IWindowSession windowlessSession = null;
if (windowlessSession == null) { // 对于应用来说 windowlessSession 是为null的,在ViewRootImpl的构造函数中通过WindowManagerGlobal.getWindowSession()作为windowSession
root = new ViewRootImpl(view.getContext(), display); //创建ViewRootImpl类对象
} else {
root = new ViewRootImpl(view.getContext(), display,windowlessSession);
}
}
上面的代码中声明一个 IWindowSession 类型的对象 windowlessSession,如果为空,则在ViewRootImpl中通过WindowManagerGlobal.getWindowSession()获取。
9, 在frameworks/base/core/java/android/view/ViewRootImpl.java中
public ViewRootImpl(Context context, Display display) {
this(context, display, WindowManagerGlobal.getWindowSession(),
false /* useSfChoreographer */);
}
从上面的代码可以看到调用了WindowManagerGlobal类的 getWindowSession()函数。
10,在frameworks/base/core/java/android/view/WindowManagerGlobal.java中
@UnsupportedAppUsage
private static WindowManagerGlobal sDefaultWindowManager;
@UnsupportedAppUsage
private static IWindowManager sWindowManagerService;
@UnsupportedAppUsage
private static IWindowSession sWindowSession;
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
// 获取IWindowManager的实例,这是一个与窗口管理器服务通信的接口
IWindowManager windowManager = getWindowManagerService();
// 打开一个新的窗口会话,并传入一个IWindowSessionCallback.Stub的匿名实现类作为回调。这个回调用于处理动画比例变化的事件
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
});
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
上面的代码中WindowManagerGlobal::getWindowSession 返回的就是一个 Session 对象。Session 继承 IWindowSession.Stub,并且内部持有 WMS 引用。上面的函数调用 getWindowManagerService 函数,在获取了WMS后,调用WMS的 openSession 函数。
public static IWindowManager getWindowManagerService() {
// synchronized (WindowManagerGlobal.class) 确保了对WindowManagerGlobal类级别的同步。这意味着在同一时间内,只有一个线程可以执行这个同步块内的代码。这通常用于保护静态变量的初始化过程,避免多线程环境下的竞争条件。
synchronized (WindowManagerGlobal.class) {
if (sWindowManagerService == null) {
// 通过ServiceManager.getService("window")获取名为"window"的系统服务,并使用IWindowManager.Stub.asInterface()方法将这个服务的代理对象转换为IWindowManager接口实例。
sWindowManagerService = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
try {
if (sWindowManagerService != null) {
ValueAnimator.setDurationScale(
sWindowManagerService.getCurrentAnimatorScale());
sUseBLASTAdapter = sWindowManagerService.useBLAST(); // 通过IWindowManager接口检查系统是否使用BLAST(一种Android的渲染技术)。
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowManagerService;
}
}
下面看下 openSession 函数。
11,在frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java中
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
@Override
public IWindowSession openSession(IWindowSessionCallback callback) {
return new Session(this, callback);
}
}
上面的代码中 new Session 传入的的第一个参数即是WMS对象。
12, 在frameworks/base/service/core/java/com/android/server/wm/Session.java中
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
final WindowManagerService mService;
final IWindowSessionCallback mCallback;
public Session(WindowManagerService service, IWindowSessionCallback callback) {
mService = service;
mCallback = callback;
}
}
面的代码中 Session 是一个匿名 Binder (没有注册到 ServiceManager).
上面分析了 mWindowSession 的获取过程,下面接着分析 addToDisplayAsUser 函数。
首先看 addToDisplayAsUser 函数的第一个参数是 IWindow,在 setView 中调用 addToDisplayAsUser 时传入的是mWindow,其声明是final W mWindow;
在ViewRootImpl的构造函数中, mWindow = new W(this);
13, 在frameworks/base/core/java/android/view/ViewRootImpl.java中
final W mWindow;
public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session, boolean useSfChoreographer) {
mWindowSession = session;
mWindow = new W(this);
}
static class W extends IWindow.Stub {
private final WeakReference<ViewRootImpl> mViewAncestor;
private final IWindowSession mWindowSession;
W(ViewRootImpl viewAncestor) {
mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
mWindowSession = viewAncestor.mWindowSession;
}
}
ViewRootImpl 下的 mWindow 是其内部类 W 的对象,这个 W 继承了 IWindow.Stub,那也是用于 binder 通信的,这个 W 是个匿名 Binder 作为 BN 端,用于 WMS 调用应用端的相关方法,W 内部有一个 ViewRootImpl 弱引用。
上面分析了 addToDisplayAsUser 函数的第一个参数,下面看下 addToDisplayAsUser 的函数体,在函数内部调用了WMS的 addWindow 函数。
14,在frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java中
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,int displayId, int requestUserId, InsetsVisibilities requestedVisibilities,
InputChannel outInputChannel, InsetsState outInsetsState,InsetsSourceControl[] outActiveControls) {
// 权限检查
int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,appOp);
if (res != ADD_OKAY) {
return res;
}
WindowState parentWindow = null; // 父窗口,应用 Activity 窗口逻辑是没有父窗口的
final int type = attrs.type; // 拿到当前窗口类型
synchronized (mGlobalLock) {
if (mWindowMap.containsKey(client.asBinder())) { // 如果窗口已经添加,直接return
ProtoLog.w(WM_ERROR, "Window %s is already added", client);
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
ActivityRecord activity = null;
final boolean hasParent = parentWindow != null; // 是否hasParent
// 拿到token
WindowToken token = displayContent.getWindowToken(hasParent ? parentWindow.mAttrs.token : attrs.token);
// If this is a child window, we want to apply the same type checking rules as the parent window type.
final int rootType = hasParent ? parentWindow.mAttrs.type : type;
if (token == null) {
if (!unprivilegedAppCanCreateTokenWith(parentWindow, callingUid, type,
rootType, attrs.token, attrs.packageName)) {
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (hasParent) {
// Use existing parent window token for child windows.
token = parentWindow.mToken; // 子窗口用父窗口的 token
} else if (mWindowContextListenerController.hasListener(windowContextToken)) {
...
} else {
// 系统窗口会创建 token
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
token = new WindowToken.Builder(this, binder, type)
.setDisplayContent(displayContent)
.setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
.setRoundedCornerOverlay(isRoundedCornerOverlay)
.build();
}
}
// 创建 WindowState
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
// 调整window的参数
displayPolicy.adjustWindowParamsLw(win, win.mAttrs);
// 验证Window是否可以添加,主要是验证权限
res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
if (res != ADD_OKAY) {
return res;
}
final boolean openInputChannels = (outInputChannel != null
&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
// Input 事件输入通道
win.openInputChannel(outInputChannel);
}
win.attach(); // 创建SurfaceSession
// client是应用端 ViewRootImpl 下的 “W”这个类,也就是说在 WMS 中应用端的这个 ViewRootImpl 和为其创建的 WindowState 已经被记录在 mWindowMap 中了。
mWindowMap.put(client.asBinder(), win); // 窗口存入mWindowMap,其中 key 是一个 IBinder,value 是 WindowState
// 窗口挂载
win.mToken.addWindow(win); // 窗口的挂载,win.mToken中这里的 mToken 刚刚看到是创建 WindowState 的时候传递的 token 也就是 ActivityRecord (WindowToken)。
displayPolicy.addWindowLw(win, attrs);
// 处理窗口焦点切换
boolean focusChanged = false;
if (win.canReceiveKeys()) {
focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
false /*updateInputWindows*/);
if (focusChanged) {
imMayMove = false;
}
}
// Don't do layout here, the window must call
// relayout to be displayed, so we'll do it there.
win.getParent().assignChildLayers(); // 调整父容器下的元素层级
if (focusChanged) { // 更新inut焦点
displayContent.getInputMonitor().setInputFocusLw(displayContent.mCurrentFocus,
false /*updateInputWindows*/);
}
}
return res;
}
上面的代码中 addWindow 是核心方法,主要有四个逻辑如下:
1)校验处理:操作权限校验;为限制应用端的一个 RootView 只能执行一次 addWindow。
2)Token 处理:这个 token 其实就是 WindowToken(ActivityRecord 是其子类);获取 token,如果是子窗口就从父窗口拿,没有就从参数里拿;如果是系统窗口就会根据窗口类型创建出一个 WindowToken。
3)WindowState 处理:创建 WindowState;执行 WindowState::attach 会创建 SurfaceSession;将新建的 WindowState 和 W 映射,存入 mWindowMap;窗口挂载。
4)Input 和焦点处理。
在上面的代码中,获取 token 也是一个重要的步骤,从代码语句WindowToken token = displayContent.getWindowToken(hasParent ? parentWindow.mAttrs.token : attrs.token);看是从attrs中获取,这个参数是从应用端传递过来的。
在WindowManagerGlobal类中执行 addView 之前,先执行了 adjustLayoutParamsForSubWindow 函数,在这个函数中对这个token进行了赋值。
15,在frameworks/base/core/java/android/view/Window.java中
private IBinder mAppToken;
private Window mContainer;
void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {
if (wp.token == null) {
wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
}
}
在上面的代码中出现了 mAppToken 变量 和 mContainer 变量,mAppToken是在 setWindowManager 中赋值的。
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken; //对mAppToken进行赋值
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated;
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
mContainer是在 setContainer 中赋值的。
public void setContainer(Window container) {
mContainer = container;
if (container != null) {
// Embedded screens never have a title.
mFeatures |= 1<<FEATURE_NO_TITLE;
mLocalFeatures |= 1<<FEATURE_NO_TITLE;
container.mHasChildren = true;
}
}
当前并没有调用 setContainer,所以在 adjustLayoutParamsForSubWindow 函数中token取值为mAppToken。而这个 mAppToken 来自于 setWindowManager 中的参数。
setWindowManager 是在Activity类中的attach方法中调用的。
final void attach(Context context, ActivityThread aThread,Instrumentation instr, IBinder token, ...) {
mToken = token; // 设置token为 ActivityRecord
mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),mToken,...);
}
上面的attach方法是在 ActivityThread 类中的 performLaunchActivity 调用的。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
Activity activity = null;
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);//创建新的activity
Window window = null; //定义window
//attach函数的第四个参数即是token
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.java中的attach方法,创建window
return activity;
}
上面代码中 performLaunchActivity 第一个参数 ActivityClientRecord 是在ActivityThread.java中定义的内部类,即
public static final class ActivityClientRecord {
public IBinder token;
public ActivityClientRecord() {
this.isForward = false;
init();
}
public ActivityClientRecord(IBinder token,...) {
this.token = token;
}
}
上面的代码中,token就是来自 ActivityClientRecord,如果该token为空,则会在WMS类中的 addWindow 函数中生成token.
下面看下 ActivityClientRecord 和 ActivityRecord的关系:
ActivityRecord:是ActivityStack的管理对象,每个Activity在ActivityManagerService(AMS)中都有一个对应的ActivityRecord。存在于system_server进程中,用于记录Activity的状态以及其他相关信息。可以理解为Activity在服务端的Activity对象的映射。
ActivityClientRecord:与ActivityRecord相对应,是Activity在客户端(ActivityThread)的记录。存在于App进程中,与Activity实例紧密相关。
ActivityRecord、ActivityClientRecord与Activity之间存在严格的一一对应关系,即每一个Activity在AMS中都有一个ActivityRecord,在App进程中都有一个ActivityClientRecord与之对应。
在Activity启动流程中,system_server进程会创建ActivityRecord,并通过特定的机制(如appToken)将其与App进程中的ActivityClientRecord和Activity关联起来。
appToken是一个可以跨进程传输的Binder对象,它持有ActivityRecord的弱引用。通过appToken,system_server进程可以与App进程中的ActivityClientRecord和Activity进行通信和状态同步。
ActivityRecord、ActivityClientRecord与Activity的生命周期是相关联的。当Activity发生变化时(如启动、暂停、停止、销毁等),对应的ActivityRecord和ActivityClientRecord也会相应地更新其状态。由于ActivityRecord和ActivityClientRecord之间存在关联机制,因此它们可以实时地同步Activity的状态信息。这使得AMS能够准确地掌握每个Activity的状态,从而进行有效的管理和调度。
上面分析了token之后,下面分析下WindowState的创建。 在WMS的addWindow函数中,通过 WindowState win = new WindowState 创建一个 WindowState 对象 win,通过该wind对象调用 attach 函数以创建SurfaceSession.
16,在frameworks/base/services/core/java/com/android/server/wm/WindowState.java中
final Session mSession; //在WindowState的构造函数中通过传入的Session值进行初始化
void attach() {
if (DEBUG) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
mSession.windowAddedLocked();
}
上面的代码调用了 Session 类的 windowAddedLocked 函数。
17,在frameworks/base/services/core/java/com/android/server/wm/Session.java中
SurfaceSession mSurfaceSession;
void windowAddedLocked() {
if (mPackageName == null) {
final WindowProcessController wpc = mService.mAtmService.mProcessMap.getProcess(mPid);
if (wpc != null) {
mPackageName = wpc.mInfo.packageName;
mRelayoutTag = "relayoutWindow: " + mPackageName;
mUpdateViewVisibilityTag = "updateVisibility: " + mPackageName;
mUpdateWindowLayoutTag = "updateLayout: " + mPackageName;
} else {
Slog.e(TAG_WM, "Unknown process pid=" + mPid);
}
}
if (mSurfaceSession == null) {
if (DEBUG) {
Slog.v(TAG_WM, "First window added to " + this + ", creating SurfaceSession");
}
mSurfaceSession = new SurfaceSession();
ProtoLog.i(WM_SHOW_TRANSACTIONS, " NEW SURFACE SESSION %s", mSurfaceSession);
mService.mSessions.add(this);
if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
mService.dispatchNewAnimatorScaleLocked(this);
}
}
mNumWindow++;
}
在上面的Session类的 windowAddedLocked 函数代码中,通过 new SurfaceSession()创建了SurfaceSession类型的对象mSurfaceSession,用于和surface flinger进行连接。
18,在frameworks/base/core/java/android/view/SurfaceSession.java中
public final class SurfaceSession {
private long mNativeClient; // SurfaceComposerClient*
private static native long nativeCreate();
private static native void nativeDestroy(long ptr);
/** Create a new connection with the surface flinger. */
@UnsupportedAppUsage
public SurfaceSession() {
mNativeClient = nativeCreate();
}
/* no user serviceable parts here ... */
@Override
protected void finalize() throws Throwable {
try {
kill();
} finally {
super.finalize();
}
}
@UnsupportedAppUsage
public void kill() {
if (mNativeClient != 0) {
nativeDestroy(mNativeClient);
mNativeClient = 0;
}
}
}
分析了WindowState的attach函数后,接着分析 win.mToken.addWindow(win)语句。
win对象为WindowState类的对象,在WindowState类中声明有@NonNull WindowToken mToken;这里的 mToken 刚刚看到是创建 WindowState 的时候传递的 token 也就是 ActivityRecord (WindowToken)。
在ActivityRecord类的 addWindow 函数中直接调用其父类 WindowToken的函数,所以代码会走到WindowToken类的 addWindow 函数中。
19, 在frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java中
final class ActivityRecord extends WindowToken implements WindowManagerService.AppFreezeListener {
void addWindow(WindowState w) {
super.addWindow(w);
}
}
20,在frameworks/base/services/core/java/com/android/server/wm/WindowToken.java中
class WindowToken extends WindowContainer<WindowState> {
void addWindow(final WindowState win) {
ProtoLog.d(WM_DEBUG_FOCUS,
"addWindow: win=%s Callers=%s", win, Debug.getCallers(5));
if (win.isChildWindow()) { // // 子窗口的父类应该是WindowState所以不执行后续
// Child windows are added to their parent windows.
return;
}
// This token is created from WindowContext and the client requests to addView now, create a
// surface for this token.
if (mSurfaceControl == null) {
createSurfaceControl(true /* force */);
// Layers could have been assigned before the surface was created, update them again
reassignLayer(getSyncTransaction());
}
if (!mChildren.contains(win)) { // 添加进子容器
ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Adding %s to %s", win, this);
addChild(win, mWindowComparator); // 挂载(添加进孩子容器),有一个比较方法
mWmService.mWindowsChanged = true; // 记录有窗口边框
// TODO: Should we also be setting layout needed here and other places?
}
}
}
执行完 WindowContainer::addChild 方法后 WindowState 已经被添加到层级树中了,挂在到对应的 ActivityRecord 下。 当然这里需要注意 WindowToken::addWindow 最终也是调用父类 WindowContainer::addChild 将 WindowState 添加到自己的孩子中,这里传递了一个 mWindowComparator。
private final Comparator<WindowState> mWindowComparator =
(WindowState newWindow, WindowState existingWindow) -> {
final WindowToken token = WindowToken.this;
if (newWindow.mToken != token) {
throw new IllegalArgumentException("newWindow=" + newWindow
+ " is not a child of token=" + token);
}
if (existingWindow.mToken != token) {
throw new IllegalArgumentException("existingWindow=" + existingWindow
+ " is not a child of token=" + token);
}
return isFirstChildWindowGreaterThanSecond(newWindow, existingWindow) ? 1 : -1;
};
protected boolean isFirstChildWindowGreaterThanSecond(WindowState newWindow,
WindowState existingWindow) {
// New window is considered greater if it has a higher or equal base layer.
return newWindow.mBaseLayer >= existingWindow.mBaseLayer; // 就是比较两个窗口的mBaseLayer,正常情况都是按顺添加,也就是后添加的在最上面。
}
上面的代码调用了 WindowContainer::addChild 方法。
21,在frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java中
class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
implements Comparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable,
InsetsControlTarget {
protected final WindowList<E> mChildren = new WindowList<E>();
@CallSuper
protected void addChild(E child, Comparator<E> comparator) {
if (!child.mReparenting && child.getParent() != null) {
throw new IllegalArgumentException("addChild: container=" + child.getName()
+ " is already a child of container=" + child.getParent().getName()
+ " can't add to container=" + getName());
}
int positionToAdd = -1; // 初始化插入位置为-1,表示尚未找到合适的插入位置
if (comparator != null) {
final int count = mChildren.size();
for (int i = 0; i < count; i++) { // 遍历当前容器中的所有子元素
if (comparator.compare(child, mChildren.get(i)) < 0) { // 如果比较结果小于0,表示待插入元素应该位于当前元素之前
positionToAdd = i;
break;
}
}
}
if (positionToAdd == -1) { // 没有比较器或者比较的结果还是-1 ,则添加到最后(大部分场景)
mChildren.add(child);
} else { // 如果比较器计算出了准确位置,则按要求添加
mChildren.add(positionToAdd, child);
}
// Set the parent after we've actually added a child in case a subclass depends on this.
child.setParent(this); // 调用孩子容器设置当前容器为其父节点
}
}
上面代码中的 addChild 函数,方法目的就是添加子元素到父容器中,但是可以根据 comparator 比较规则添加到正确的位置,比较方式很简单,拿当前需要添加的元素和容器内其他元素逐个比较,如果比较 comparator 返回值小于0,则添加到被比较的元素前面。
当前逻辑调用的比较器是 WindowToken 下的 mWindowComparator。
至此,addWindow 的流程已经完成。