接着上篇:https://www.jianshu.com/p/67a61225fdc7?v=1698053234018
三、代码流程详解
1.客户端
1.1 Activity走到onresume后
从ActivityThread.handleResumeActivity方法看起
1.调用performResumeActivity,执行onResume。
2.获取WindowManager的实现类WindowManagerImpl的实例。
3.调用WindowManagerImpl.addView传入DecorView和当前布局参数WindowManager.LayoutParams。
代码路径:framework/core/java/android/app/ActivityThread.java
@Override
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
boolean isForward, String reason) {
......
// TODO Push resumeArgs into the activity for consideration
// skip below steps for double-resume and r.mFinish = true case.
/*1.执行onResume*/
if (!performResumeActivity(r, finalStateRequest, reason)) {
return;
}
......
//获取Activity实例
final Activity a = r.activity;
......
// If the window hasn't yet been added to the window manager,
// and this guy didn't finish itself or start another activity,
// then go ahead and add the window.
//mStartedActivity在performLaunchActivity和performResumeActivity方法中被置为false
boolean willBeVisible = !a.mStartedActivity;
......
if (r.window == null && !a.mFinished && willBeVisible) {
//获取当前Activity的PhoneWindow
r.window = r.activity.getWindow();
//从PhoneWindow中获取DecorView
View decor = r.window.getDecorView();
//将view的可见性状态设置为INVISIBLE,view不可见但是仍然占用布局空间
decor.setVisibility(View.INVISIBLE);
/*2.获取WindowManager的实现类WindowManagerImpl的实例*/
ViewManager wm = a.getWindowManager();
//获取布局参数
WindowManager.LayoutParams l = r.window.getAttributes();
//将phoneWindow的DecorView赋值给mDecor
a.mDecor = decor;
//设置窗口类型为TYPE_BASE_APPLICATION
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
// Normally the ViewRoot sets up callbacks with the Activity
// in addView->ViewRootImpl#setView. If we are instead reusing
// the decor view we have to notify the view root that the
// callbacks may have changed.
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
/*3.传入DecorView和当前布局参数WindowManager.LayoutParams*/
wm.addView(decor, l);
} else {
// The activity will get a callback for this {@link LayoutParams} change
// earlier. However, at that time the decor will not be set (this is set
// in this method), so no action will be taken. This call ensures the
// callback occurs with the decor set.
a.onWindowAttributesChanged(l);
}
}
}
......
}
wm.addView(decor, l);WindowManager接口的实现是WindowManagerImpl,即实际调用的是WindowManagerImpl中的addView方法
代码路径:framework/core/java/android/view/WindowManagerImpl.java
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyTokens(params);
//转交给windowManagerGlobal,添加view
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
mContext.getUserId());
}
WindowManagerImpl对窗口的管理交给WindowManagerGlobal,调用WindowManagerGlobal的addView方法
WindowManagerGlobal中对窗口的处理主要如下几个步骤:
1.对WindowManagerImpl传进来的参数进行检查。
2.设置WindowManager.LayoutParams中的token、title等相关属性。查看“【1.2 Token的创建与传递】”。
3.创建ViewRootImpl对象,并获取客户端与WMS通信的Session。查看“【1.3 ViewRootImpl的创建】”。
4.在WindowManagerGlobal中备份DecorView,WindowManager.LayoutParams以及ViewRootImpl。
5.调用ViewRootImpl,与WMS通信添加窗口。查看“【1.4 ViewRootImpl与WMS的通信】”。
代码路径:framework/core/java/android/view/WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {
/*1.对WindowManagerImpl传进来的参数进行检查*/
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (display == null) {
throw new IllegalArgumentException("display must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
//此处的ParentWindow即当Activity的PhoneWindow
if (parentWindow != null) {
/*2.为wparams的token进行赋值*/
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
......
}
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
......
IWindowSession windowlessSession = null;
......
if (windowlessSession == null) {
/*3.新建ViewRootImpl,在新建时会通过WindowManagerGlobal获取session*/
root = new ViewRootImpl(view.getContext(), display);
} else {
root = new ViewRootImpl(view.getContext(), display,
windowlessSession);
}
view.setLayoutParams(wparams);
/*4.在WindowManagerGlobal中备份DecorView,WindowManager.LayoutParams以及ViewRootImpl。*/
//当前view加入到view列表中
mViews.add(view);
//将新建的viewRootImpl加入到root列表中
mRoots.add(root);
//将当前布局参数加入到布局参数列表中
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
/*5.调用ViewRootImpl,设置view,panelParentView为null,与WMS通信添加窗口*/
root.setView(view, wparams, panelParentView, userId);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
1.2 Token的创建与传递
parentWindow.adjustLayoutParamsForSubWindow(wparams);调用Window的adjustLayoutParamsForSubWindow()方法
在adjustLayoutParamsForSubWindow中会分别对WindowManager.LayoutParams中的token以及title进行赋值。
1.首先针对子窗口、系统窗口以及应用窗口做了不同的处理,此处我们只关注应用窗口的处理。
2.其次将当前PhoneWindow.mAppToken赋值给WindowManager.LayoutParams.token。
代码路径:framework/core/java/android/view/Window.java
void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {
CharSequence curTitle = wp.getTitle();
if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
//对子窗口的Token以及Title赋值
......
} else if (wp.type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW &&
wp.type <= WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) {
//对子窗口的Token以及Title赋值
......
} else {
//对应用窗口的Token以及Title赋值
if (wp.token == null) {
//将当前PhoneWindow的mAppToken赋值给wp.Token
wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
}
//将Title设置为mAppName
if ((curTitle == null || curTitle.length() == 0)
&& mAppName != null) {
wp.setTitle(mAppName);
}
}
//设置为packageName
if (wp.packageName == null) {
wp.packageName = mContext.getPackageName();
}
......
}
此处的mAppToken便是在Activity启动时,在ATMS端创建的Token。
接下来我们看看Token是如何从ATMS端传递过来,并赋值给PhoneWindow.mAppToken的
1.在ATMS端新建ActivityRecord时,便新建了Token。并赋值给ActivityRecord.token
ActivityRecord继承WindowToken
代码路径:framework/services/core/java/com/android/server/wm/ActivityRecord.java
private ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller,
int _launchedFromPid, int _launchedFromUid, String _launchedFromPackage,
@Nullable String _launchedFromFeature, Intent _intent, String _resolvedType,
ActivityInfo aInfo, Configuration _configuration, ActivityRecord _resultTo,
String _resultWho, int _reqCode, boolean _componentSpecified,
boolean _rootVoiceInteraction, ActivityTaskSupervisor supervisor,
ActivityOptions options, ActivityRecord sourceRecord, PersistableBundle persistentState,
TaskDescription _taskDescription, long _createTime) {
//新建Token
super(_service.mWindowManager, new Token(), TYPE_APPLICATION, true,
null /* displayContent */, false /* ownerCanManageAppTokens */);
......
}
2.将ActivityRecord.token封装在clientTransaction中,并将这个传递到客户端
代码路径:framework/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
boolean andResume, boolean checkConfig) throws RemoteException {
......
final Task task = r.getTask();
final Task rootTask = task.getRootTask();
......
try {
......
try {
......
// Create activity launch transaction.
/*将ActivityRecord.token封装在clientTransaction中*/
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));
......
// Schedule transaction.
/*将clientTransaction传递给客户端*/
mService.getLifecycleManager().scheduleTransaction(clientTransaction);
......
} catch (RemoteException e) {
......
}
} finally {
......
}
......
return true;
}
final ClientTransaction clientTransaction = ClientTransaction.obtain( proc.getThread(), r.token);
在ClientTransaction中调用obtain方法,把ActivityRecord.token存到mActivityToken
代码路径:framework/core/java/android/app/servertransaction/ClientTransaction.java
/** Obtain an instance initialized with provided params. */
public static ClientTransaction obtain(IApplicationThread client, IBinder activityToken) {
ClientTransaction instance = ObjectPool.obtain(ClientTransaction.class);
if (instance == null) {
//创建ClientTransaction
instance = new ClientTransaction();
}
instance.mClient = client;
/*把ActivityRecord.token存到mActivityToken*/
//private IBinder mActivityToken;
instance.mActivityToken = activityToken;
return instance;
}
3.客户端从ClientTransaction中获取ATMS端传来的Token,并传递到LaunchActivityItem中
代码路径:framework/core/java/android/app/servertransaction/TransactionExecutor.java
/** Cycle through all states requested by callbacks and execute them at proper times. */
@VisibleForTesting
public void executeCallbacks(ClientTransaction transaction) {
final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
......
/*从ClientTransaction中获取ATMS端传来的Token*/
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);
......
/*将Token传递到LaunchActivityItem中*/
item.execute(mTransactionHandler, token, mPendingActions);
item.postExecute(mTransactionHandler, token, mPendingActions);
......
}
}
4.在LaunchActivityItem中将客户端传过来的Token保存在ActivityClientRecord.token中
代码路径:framework/core/java/android/app/servertransaction/LaunchActivityItem.java
@Override
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
//将客户端传过来的Token保存在ActivityClientRecord的token中
ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,
client, mAssistToken, mShareableActivityToken, mLaunchedFromBubble,
mTaskFragmentToken);
client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
client.handleLaunchActivity(r, pendingActions, null /* customIntent */);ClientTransactionHandler调用handleLaunchActivity方法,ClientTransactionHandler为抽象类,其子类为ActivityThread,即实际调用的是该类中的handleLaunchActivity(),有从该方法中调用到了performLaunchActivity()
5.客户端ActivityThread将ActivityClientRecord以及其对应的token保存在ActivityThread.mActivities数组中,并调用Activity.attach将Token传给Activity。
代码路径:framework/core/java/android/app/ActivityThread.java
/** Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
......
try {
Application app = r.packageInfo.makeApplicationInner(false, mInstrumentation);
......
synchronized (mResourcesManager) {
/*将ActivityClientRecord以及其对应的Token保存在mActivities中*/
//mActivities的类型为ArrayMap<IBinder, ActivityClientRecord>
mActivities.put(r.token, r);
}
if (activity != null) {
......
/*将Token赋值给Activity.mToken*/
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);
......
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
......
}
return activity;
}
6.在Activity中将客户端传来的Token赋值给Activity.mToken。此外在该方法中还新建了PhoneWindow,并将PhoneWindow.mAppToken也设置为客户端传过来的Token。
代码路径:framework/core/java/android/app/Activity.java
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
IBinder shareableActivityToken) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
/*新建PhoneWindow*/
mWindow = new PhoneWindow(this, window, activityConfigCallback);
......
/*将客户端传过来的Token赋值给mToken*/
mToken = token;
......
/*PhoneWindow.mAppToken设置为当前客户端传递过来的Token*/
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
......
}
PhoneWindow继承Window,setWindowManager实际调用的是其父类方法,把mAppToken设置为当前客户端传递过来的mToken
代码路径:framework/core/java/android/view/Window.java
/**
* Set the window manager for use by this Window to, for example,
* display panels. This is <em>not</em> used for displaying the
* Window itself -- that must be done by the client.
*
* @param wm The window manager for adding new windows.
*/
public void setWindowManager(WindowManager wm, IBinder appToken, String appName) {
//传递客户端的mToken给appToken
setWindowManager(wm, appToken, appName, false);
}
/**
* Set the window manager for use by this Window to, for example,
* display panels. This is <em>not</em> used for displaying the
* Window itself -- that must be done by the client.
*
* @param wm The window manager for adding new windows.
*/
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
/*把appToken赋值给mAppToken*/
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated;
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
1.3 ViewRootImpl的创建
root = new ViewRootImpl(view.getContext(), display);之前在【1.1 Activity走到onresume后】的流程中有调用创建ViewRootImpl,这里我们看下ViewRootImpl的构造方法
代码路径:framework/core/java/android/view/ViewRootImpl.java
public ViewRootImpl(Context context, Display display) {
this(context, display, WindowManagerGlobal.getWindowSession(),
false /* useSfChoreographer */);
}
public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session) {
this(context, display, session, false /* useSfChoreographer */);
}
public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session,
boolean useSfChoreographer) {
mContext = context;
mWindowSession = session;
......
}
从这个构造方法中我们可以看出,通过WindowManagerGlobal.getWindowSession获取到客户端与WMS沟通的桥梁IWindowSession,并将其赋值给ViewRootImpl.mWindowSession。
下面我们查看WindowManagerGlobal中是如何获取Session的。
1.通过getWindowManagerService获取IWindowManager,而WindowManagerService则实现了这个Binder接口。
2.调用IWindowManager.openSession方法即WMS.openSession,在WMS端便会新建Session。至此客户端与WMS通信的桥梁便已经搭建好了
代码路径:framework/core/java/android/view/WindowManagerGlobal.java
@UnsupportedAppUsage
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
// Emulate the legacy behavior. The global instance of InputMethodManager
// was instantiated here.
// TODO(b/116157766): Remove this hack after cleaning up @UnsupportedAppUsage
InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
/*1.获取Binder*/
IWindowManager windowManager = getWindowManagerService();
/*2.调用WMS的openSession*/
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
});
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
从代码中可以看出如果sWindowSession不为空则直接返回,sWindowSession为当前WindowManagerGlobal属性,且WindowManagerGloba又是单例的,所以客户端一个进程中只有一个IWindowSession与WMS通信。如果sWindowSession为空,则会创建IWindowSession。
调用WindowManagerService中的openSession,新建Session
代码路径:framework/services/core/java/com/android/server/wm/WindowManagerService.java
@Override
public IWindowSession openSession(IWindowSessionCallback callback) {
/*新建Session*/
return new Session(this, callback);
}
1.4 ViewRootImpl与WMS的通信
root.setView(view, wparams, panelParentView, userId);之前在【1.1 Activity走到onresume后】的流程中有调用ViewRootImpl与WMS的通信,继续看看
当前方法是与WMS进行通信添加窗口的入口,在此处我们只关注两点:
1.requestLayout()该方法会调用到doTraversal(),之后调用performTraversals(),最终调用到relayoutWindow()和reportDrawFinished()流程,在通过Session与服务端通信
2.mWindowSession.addToDisplayAsUser,与服务端进行Binder通信,调用Session的addToDisplayAsUser方法。
/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
setView(view, attrs, panelParentView, UserHandle.myUserId());
}
/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
synchronized (this) {
if (mView == null) {
mView = view;
......
//将布局参数拷贝纸mWindowAttributes
mWindowAttributes.copyFrom(attrs);
//设置包名
if (mWindowAttributes.packageName == null) {
mWindowAttributes.packageName = mBasePackageName;
}
mWindowAttributes.privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
attrs = mWindowAttributes;
......
// Keep track of the actual window flags supplied by the client.
//获取当前布局的flags
mClientWindowLayoutFlags = attrs.flags;
......
int res; /* = WindowManagerImpl.ADD_OKAY; */
// 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.
/*请求布局,对应服务端layoutWindow流程*/
requestLayout();
InputChannel inputChannel = null;
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
inputChannel = new InputChannel();
}
......
try {
......
/*与服务端进行Binder通信,调用Session的addToDisplayAsUser方法*/
//执行addWindow的相关流程
res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId,
mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,
mTempControls);
......
} catch (RemoteException e) {
......
} finally {
if (restore) {
attrs.restore();
}
}
......
if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
if (res < WindowManagerGlobal.ADD_OKAY) {
mAttachInfo.mRootView = null;
mAdded = false;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
switch (res) {
case WindowManagerGlobal.ADD_BAD_APP_TOKEN:
case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:
throw new WindowManager.BadTokenException(
"Unable to add window -- token " + attrs.token
+ " is not valid; is your activity running?");
case WindowManagerGlobal.ADD_NOT_APP_TOKEN:
throw new WindowManager.BadTokenException(
"Unable to add window -- token " + attrs.token
+ " is not for an application");
case WindowManagerGlobal.ADD_APP_EXITING:
throw new WindowManager.BadTokenException(
"Unable to add window -- app for token " + attrs.token
+ " is exiting");
case WindowManagerGlobal.ADD_DUPLICATE_ADD:
throw new WindowManager.BadTokenException(
"Unable to add window -- window " + mWindow
+ " has already been added");
case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED:
// Silently ignore -- we would have just removed it
// right away, anyway.
return;
case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON:
throw new WindowManager.BadTokenException("Unable to add window "
+ mWindow + " -- another window of type "
+ mWindowAttributes.type + " already exists");
case WindowManagerGlobal.ADD_PERMISSION_DENIED:
throw new WindowManager.BadTokenException("Unable to add window "
+ mWindow + " -- permission denied for window type "
+ mWindowAttributes.type);
case WindowManagerGlobal.ADD_INVALID_DISPLAY:
throw new WindowManager.InvalidDisplayException("Unable to add window "
+ mWindow + " -- the specified display can not be found");
case WindowManagerGlobal.ADD_INVALID_TYPE:
throw new WindowManager.InvalidDisplayException("Unable to add window "
+ mWindow + " -- the specified window type "
+ mWindowAttributes.type + " is not valid");
case WindowManagerGlobal.ADD_INVALID_USER:
throw new WindowManager.BadTokenException("Unable to add Window "
+ mWindow + " -- requested userId is not valid");
}
throw new RuntimeException(
"Unable to add window -- unknown error code " + res);
}
......
}
}
}
其中关键的添加代码为
res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId,
mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,
mTempControls);
addToDisplayAsUser()方法最终会走到WindowManagerService.java的addWindow方法,addWindow方法的返回值最后会返回给res,之后回看ViewRootImpl的setView方法,返回值如果满足if (res < WindowManagerGlobal.ADD_OKAY)条件,那么会根据switch (res)中对应的case抛出异常。
至此,客户端流程结束,后面进入服务端流程。
————————————————
版权声明:本文为CSDN博主「yi诺千金」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yimelancholy/article/details/130339779