preface
With the development of mobile terminal and front-end technology, more and more applications use the mode of mixed development, the most typical representative is small program. Some of this will definitely involve front-end and mobile interaction.
The common interaction modes between Android and the front end are as follows:
JavaScript to Android | The Android – JavaScript |
---|---|
addJavascriptInterface | loadUrl |
shouldOverrideUrlLoading | evaluateJavascript |
onJsPrompt/onJsConfirm/onJsAlert | / |
In the development of Android and front-end interaction, due to front-end performance and style and other related reasons, some controls will be displayed through the front-end input Android native display. Such as Dialog, DatePicker, Alert, Toast, etc. Which brings us to today’s question.
Front end: I found a popover control does not display, help me check the reason.
Android: generally pop-ups are the communication between JS and Android, we will intercept it in addJavascriptInterface, and then display it. You don’t need to check, it should be my problem, LET me see.
Android: check for a long time, how did not go to addJavascriptInterface, must be JS error, front-end quick connect to the pan!
Front end: Why was the previous version ok?
Android :(guilty……) You can’t be my problem. Check it out.
After investigation, it was found that this popover did not go to the front end of the communication with Android, but used a SELECT tag, then the question arises, why the use of select suddenly failed to display correctly.
Problem description
This article source analysis based on Android API Level29
The front-end code
select.html
<! DOCTYPEhtml>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<select>
<option value="Java">Java</option>
<option value="PHP">PHP</option>
<option value="Go">Go</option>
<option value="JavaScript">JavaScript</option>
</select>
</body>
</html>
Copy the code
The Android code
MainActivity.java
public class MainActivity extends AppCompatActivity {
private WebView webView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewGroup container = findViewById(R.id.container);
webView = new WebView(getApplicationContext());
webView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));
container.addView(webView);
webView.loadUrl("file:android_asset/select.html"); }}Copy the code
This is a simplified example of code that does not pop up when clicking the Select TAB and the console displays the following error:
E/WindowManager: BadTokenException or InvalidDisplayException, clean up.
Copy the code
When the WebView is passed in a different ApplicationContext, the popup window does not display properly. Under normal circumstances, the popup window will display as shown in the following figure:
The Layout Inspector shows the Select TAB as AlertDialog on the Android layer. So let’s simplify things a little bit and pass in an Application Context directly to an AlertDialog and it might be a problem, so let’s verify that.
The verification code is simple as follows:
findViewById(R.id.show_dialog).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new AlertDialog.Builder(getApplicationContext())
.setTitle("Title")
.setMessage("message") .create() .show(); }});Copy the code
If we wanted to, the program crashed. Remember Unable to add window — token null is not valid; is your activity running? This is a line of logs that we’ll see later.
The 2021-08-03 15:36:00. 112, 19594-19594 / com. Sample. Select E/WindowManager: BadTokenException or InvalidDisplayException, Sample. Select D/AndroidRuntime: AndroidRuntime: AndroidRuntime: AndroidRuntime: AndroidRuntime: AndroidRuntime: AndroidRuntime: AndroidRuntime: AndroidRuntime: AndroidRuntime: AndroidRuntime: AndroidRuntime: AndroidRuntime: Shutting down VM --------- beginning of Crash 2021-08-03 15:36:00.113 19594-19594/com.sample.select E/AndroidRuntime: N /A FATAL EXCEPTION: main Process: com.sample.select, PID: 19594 android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?
at android.view.ViewRootImpl.setView(ViewRootImpl.java:1133)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:409)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:111)
at android.app.Dialog.show(Dialog.java:342)
at com.sample.select.MainActivityThe $1.onClick(MainActivity.java:34)
at android.view.View.performClick(View.java:7567)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1119)
at android.view.View.performClickInternal(View.java:7544)
at android.view.View.accessThe $3600(View.java:836)
at android.view.View$PerformClick.run(View.java:28770)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:233)
at android.app.ActivityThread.main(ActivityThread.java:7892)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:656)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967)
Copy the code
Dialog displays source code analysis
Since crash’s log is related to the display of Dialog, now we don’t look at the error reported above, and first follow the line showing Dialog to see the main process of Dialog calling show method.
findViewById(R.id.show_dialog).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Click the button to execute the following code, which is ActivityContext
new AlertDialog.Builder(MainActivity.this)
.setTitle("Title")
.setMessage("message") .create() .show(); }});Copy the code
1.0 AlertDialog create
SetTitle and setMessage set some display information. Focus on what create() does.
AlertDialog.java
public AlertDialog create(a) {
// Context has already been wrapped with the appropriate theme.
// Create an AlertDialog object
final AlertDialog dialog = new AlertDialog(P.mContext, 0.false);
P.apply(dialog.mAlert);
dialog.setCancelable(P.mCancelable);
if (P.mCancelable) {
dialog.setCanceledOnTouchOutside(true);
}
dialog.setOnCancelListener(P.mOnCancelListener);
dialog.setOnDismissListener(P.mOnDismissListener);
if(P.mOnKeyListener ! =null) {
dialog.setOnKeyListener(P.mOnKeyListener);
}
return dialog;
}
Copy the code
1.1 Invoke the AlertDialog constructor
Final AlertDialog dialog = new AlertDialog(P.mContext, 0, false) will eventually be called to the dialog.
Dialog.java
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
if (createContextThemeWrapper) {
if (themeResId == Resources.ID_NULL) {
final TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
themeResId = outValue.resourceId;
}
mContext = new ContextThemeWrapper(context, themeResId);
} else {
mContext = context;
}
// Get WindowManager from context
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
/ / create PhoneWindow
final Window w = new PhoneWindow(mContext);
mWindow = w;
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
w.setOnWindowSwipeDismissedCallback(() -> {
if(mCancelable) { cancel(); }});// Set the WindowManager for Windows
w.setWindowManager(mWindowManager, null.null);
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
}
Copy the code
We can see that there are three main things that are done in the Dialog constructor
- Get the WindowManager from the context.
- Create PhoneWindow and assign it to mWindow. Window is an abstract class and PhoneWindow is its only implementation class
- Set the WindowManager for the previous Window
1.2 according to the show
Create completes and goes to the show method on the Dialog
Dialog.java
public void show(a) {
// ...
if(! mCreated) { dispatchOnCreate(null);
} else {
// Fill the DecorView in on any configuration changes that
// may have occured while it was removed from the WindowManager.
final Configuration config = mContext.getResources().getConfiguration();
mWindow.getDecorView().dispatchConfigurationChanged(config);
}
onStart();
mDecor = mWindow.getDecorView();
// ...
WindowManager.LayoutParams l = mWindow.getAttributes();
boolean restoreSoftInputMode = false;
if ((l.softInputMode
& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
l.softInputMode |=
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
restoreSoftInputMode = true;
}
// Notice the mWindowManager, where the error is reported
mWindowManager.addView(mDecor, l);
if (restoreSoftInputMode) {
l.softInputMode &=
~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
}
mShowing = true;
sendShowMessage();
}
Copy the code
The main points we care about in this step are:
- Get the DecorView through mWindow
- Add a View from mWindowManager: mWindowManager.addView(mDecor, L)
So mDecor is obtained through mWindow, how is it set? Let’s take another look.
1.2.1 dispatchOnCreate
DispatchOnCreate is called in the show method as follows:
Dialog.java
void dispatchOnCreate(Bundle savedInstanceState) {
if(! mCreated) { onCreate(savedInstanceState); mCreated =true; }}Copy the code
1.2.2 onCreate
AlertDialog.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAlert.installContent();
}
Copy the code
1.2.3 installContent
AlertController.java
@UnsupportedAppUsage
public void installContent(a) {
int contentView = selectContentView();// Default layout file
mWindow.setContentView(contentView);
setupView();
}
Copy the code
1. The setContentView
PhoneWindow.java
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
/ / the key
installDecor();
} else if(! hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); }if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if(cb ! =null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
Copy the code
1.2.5 installDecor
PhoneWindow.java
private void installDecor(a) {
mForceDecorInstall = false;
if (mDecor == null) {
// Assign mDecor
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if(! mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures ! =0) { mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); }}else {
mDecor.setWindow(this);
}
// ...
}
Copy the code
1.2.6 generateDecor
PhoneWindow.java
protected DecorView generateDecor(int featureId) {
// System process doesn't have application context and in that case we need to directly use
// the context we have. Otherwise we want the application context, so we don't cling to the
// activity.
Context context;
if (mUseDecorContext) {
Context applicationContext = getContext().getApplicationContext();
if (applicationContext == null) {
context = getContext();
} else {
context = new DecorContext(applicationContext, getContext());
if(mTheme ! = -1) { context.setTheme(mTheme); }}}else {
context = getContext();
}
return new DecorView(context, featureId, this, getAttributes());
}
Copy the code
At this point the Window’s DecorView is assigned. With the subline done, we return to the main line.
1.3 WindowManagerImpl addView
You can see that mWindowManager.addView(mDecor, L) is called in show(), which is done by WindowManager to add the View as a window to the WMS.
In the process of obtaining mWindowManager we can see that the implementation class of mWindowManager is WindowManagerImpl, next to WindowManagerImpl to see the implementation of addView method.
WindowManagerImpl.java
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
Copy the code
1.4 WindowManagerGlobal addView
WindowManagerImpl calls mglobal.addView again. Mglobal.addview is a singleton with only one instance object in the current process.
WindowManagerGlobal.java
// get the WindowManagerGlobal singleton object
@UnsupportedAppUsage
public static WindowManagerGlobal getInstance(a) {
synchronized (WindowManagerGlobal.class) {
if (sDefaultWindowManager == null) {
sDefaultWindowManager = new WindowManagerGlobal();
}
returnsDefaultWindowManager; }}/ /...
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (display == null) {
throw new IllegalArgumentException("display must not be null");
}
if(! (paramsinstanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if(parentWindow ! =null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
// If there's no parent, then hardware acceleration for this view is
// set from the application's hardware acceleration setting.
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) {
// Start watching for system property changes.
if (mSystemPropertyUpdater == null) {
mSystemPropertyUpdater = new Runnable() {
@Override public void run(a) {
synchronized (mLock) {
for (int i = mRoots.size() - 1; i >= 0; --i) { mRoots.get(i).loadSystemProperties(); }}}}; SystemProperties.addChangeCallback(mSystemPropertyUpdater); }int index = findViewLocked(view, false);
if (index >= 0) {
if (mDyingViews.contains(view)) {
// Don't wait for MSG_DIE to make it's way through root's queue.
mRoots.get(index).doDie();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
// The previous removeView() had not completed executing. Now it has.
}
// If this is a panel window, then find the window it is being
// attached to for future reference.
if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
final int count = mViews.size();
for (int i = 0; i < count; i++) {
if(mRoots.get(i).mWindow.asBinder() == wparams.token) { panelParentView = mViews.get(i); }}}/ / create ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throwe; }}}Copy the code
The view wrootimpl is created in the addView method of Windows ManagerGlobal. Let’s look at what root.setView(View, wparams, panelParentView) does.
1.5 ViewRootImpl setView
ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
/ /...
int res; /* = WindowManagerImpl.ADD_OKAY; * /
requestLayout();
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = newInputChannel(); } mForceDecorViewVisibility = (mWindowAttributes.privateFlags & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) ! =0;
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
// Here we assign a value to res
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
mTempInsets);
setFrame(mTmpFrame);
} 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(); }}/ /...
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:
// The crash was caused by the code going into this branch
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");
}
throw new RuntimeException(
"Unable to add window -- unknown error code " + res);
}
/ /...}}}Copy the code
At this point, if res < WindowManagerGlobal.ADD_OKAY you can see that we have an error above
Unable to add window -- token null is not valid; is your activity running?
Here if we pass an Activity the return value is greater than zero (ADD_OKAY). The Application res value passed in is -1.
Error analysis
With the basis of the above, then we analyze how res to above, res is mWindowSession addToDisplay return values, what’s the mWindowSession?
It is clear from the following figure and code that mWindowSession is a Binder.
ViewRootImpl.java
@UnsupportedAppUsage
final IWindowSession mWindowSession;
/ /...
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
mTempInsets)
Copy the code
IWindowSession.aidl
interface IWindowSession {
int addToDisplay(IWindow window, int seq, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, out Rect outFrame,
out Rect outContentInsets, out Rect outStableInsets,
out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel,
out InsetsState insetsState, out InsetsSourceControl[] activeControls);
int addToDisplayAsUser(IWindow window, int seq, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, in int userId,
out Rect outFrame, out Rect outContentInsets, out Rect outStableInsets,
out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel,
out InsetsState insetsState, out InsetsSourceControl[] activeControls);
int addToDisplayWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, out Rect outContentInsets,
out Rect outStableInsets, out InsetsState insetsState);
@UnsupportedAppUsage
void remove(IWindow window);
int relayout(IWindow window, int seq, in WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility,
int flags, long frameNumber, out Rect outFrame,
out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets,
out Rect outBackdropFrame,
out DisplayCutout.ParcelableWrapper displayCutout,
out MergedConfiguration outMergedConfiguration, out SurfaceControl outSurfaceControl,
out InsetsState insetsState, out InsetsSourceControl[] activeControls,
out Point outSurfaceSize, out SurfaceControl outBlastSurfaceControl);
}
Copy the code
MWindowSession is a Binder that runs in the application process and is initialized when ViewRootImpl is created.
ViewRootImpl.java
public ViewRootImpl(Context context, Display display) {
mContext = context;
mWindowSession = WindowManagerGlobal.getWindowSession();
mDisplay = display;
/ /...
}
Copy the code
In ViewRootImpl constructor invokes the WindowManagerGlobal. GetWindowSession ()
WindowManagerGlobal.java
public static IWindowSession getWindowSession(a) {
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();
IWindowManager windowManager = getWindowManagerService();
s WindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) { ValueAnimator.setDurationScale(scale); }}); }catch (RemoteException e) {
throwe.rethrowFromSystemServer(); }}returnsWindowSession; }}Copy the code
IWindowSession corresponds to a Session in the system_server process.
Session.java
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
final WindowManagerService mService;
final IWindowSessionCallback mCallback;
final int mUid;
final int mPid;
private final String mStringName;
SurfaceSession mSurfaceSession;
private int mNumWindow = 0;
// Set of visible application overlay window surfaces connected to this session.
private final Set<WindowSurfaceController> mAppOverlaySurfaces = new HashSet<>();
// Set of visible alert window surfaces connected to this session.
private final Set<WindowSurfaceController> mAlertWindowSurfaces = new HashSet<>();
private final DragDropController mDragDropController;
final boolean mCanAddInternalSystemWindow;
final boolean mCanHideNonSystemOverlayWindows;
final boolean mCanAcquireSleepToken;
private AlertWindowNotification mAlertWindowNotification;
private boolean mShowingAlertWindowNotificationAllowed;
private boolean mClientDead = false;
private float mLastReportedAnimatorScale;
private String mPackageName;
private String mRelayoutTag;
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
try {
return super.onTransact(code, data, reply, flags);
} catch (RuntimeException e) {
if(! (einstanceof SecurityException)) {
Slog.wtf(TAG_WM, "Window Session Crash", e);
}
throwe; }}@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,
outInsetsState);
}
/ /...
}
Copy the code
Session addToDisplay calls mservice.addWindow (), which is the familiar WindowManagerService.
WindowManagerService.java
public int addWindow(Session session, IWindow client, int seq,
LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState) {
int[] appOp = new int[1];
int res = mPolicy.checkAddPermission(attrs, appOp);
if(res ! = WindowManagerGlobal.ADD_OKAY) {return res;
}
boolean reportNewConfig = false;
WindowState parentWindow = null;
long origId;
final int callingUid = Binder.getCallingUid();
// Assign type here
final int type = attrs.type;
synchronized (mGlobalLock) {
if(! mDisplayReady) {throw new IllegalStateException("Display has not been initialialized");
}
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
if (displayContent == null) {
Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "
+ displayId + ". Aborting.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
if(! displayContent.hasAccess(session.mUid)) { Slog.w(TAG_WM,"Attempted to add window to a display for which the application "
+ "does not have access: " + displayId + ". Aborting.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
if (mWindowMap.containsKey(client.asBinder())) {
Slog.w(TAG_WM, "Window " + client + " is already added");
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
// Assign parentWindow here
parentWindow = windowForClientLocked(null, attrs.token, false);
if (parentWindow == null) {
Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
&& parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
+ attrs.token + ". Aborting.");
returnWindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN; }}if(type == TYPE_PRIVATE_PRESENTATION && ! displayContent.isPrivate()) { Slog.w(TAG_WM,"Attempted to add private presentation window to a non-private display. Aborting.");
return WindowManagerGlobal.ADD_PERMISSION_DENIED;
}
AppWindowToken atoken = null;
final booleanhasParent = parentWindow ! =null;
// Use existing parent window token for child windows since they go in the same token
// as there parent window so we can apply the same policy on them.
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;
boolean addToastWindowRequiresToken = false;
// Token == null returns WindowManagerGlobal.ADD_BAD_APP_TOKEN
if (token == null) {
if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
Slog.w(TAG_WM, "Attempted to add application window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_INPUT_METHOD) {
Slog.w(TAG_WM, "Attempted to add input method window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_VOICE_INTERACTION) {
Slog.w(TAG_WM, "Attempted to add voice interaction window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_WALLPAPER) {
Slog.w(TAG_WM, "Attempted to add wallpaper window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_DREAM) {
Slog.w(TAG_WM, "Attempted to add Dream window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_QS_DIALOG) {
Slog.w(TAG_WM, "Attempted to add QS dialog window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (type == TYPE_TOAST) {
// Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
if (doesAddToastWindowRequireToken(attrs.packageName, callingUid,
parentWindow)) {
Slog.w(TAG_WM, "Attempted to add a toast window with unknown token "
+ attrs.token + ". Aborting.");
returnWindowManagerGlobal.ADD_BAD_APP_TOKEN; }}finalIBinder binder = attrs.token ! =null ? attrs.token : client.asBinder();
final booleanisRoundedCornerOverlay = (attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) ! =0;
token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
} else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
atoken = token.asAppWindowToken();
if (atoken == null) {
Slog.w(TAG_WM, "Attempted to add window with non-application token "
+ token + ". Aborting.");
return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
} else if (atoken.removed) {
Slog.w(TAG_WM, "Attempted to add window with exiting application token "
+ token + ". Aborting.");
return WindowManagerGlobal.ADD_APP_EXITING;
} else if(type == TYPE_APPLICATION_STARTING && atoken.startingWindow ! =null) {
Slog.w(TAG_WM, "Attempted to add starting window to token with already existing"
+ " starting window");
returnWindowManagerGlobal.ADD_DUPLICATE_ADD; }}else if (rootType == TYPE_INPUT_METHOD) {
if(token.windowType ! = TYPE_INPUT_METHOD) { Slog.w(TAG_WM,"Attempted to add input method window with bad token "
+ attrs.token + ". Aborting.");
returnWindowManagerGlobal.ADD_BAD_APP_TOKEN; }}else if (rootType == TYPE_VOICE_INTERACTION) {
if(token.windowType ! = TYPE_VOICE_INTERACTION) { Slog.w(TAG_WM,"Attempted to add voice interaction window with bad token "
+ attrs.token + ". Aborting.");
returnWindowManagerGlobal.ADD_BAD_APP_TOKEN; }}else if (rootType == TYPE_WALLPAPER) {
if(token.windowType ! = TYPE_WALLPAPER) { Slog.w(TAG_WM,"Attempted to add wallpaper window with bad token "
+ attrs.token + ". Aborting.");
returnWindowManagerGlobal.ADD_BAD_APP_TOKEN; }}else if (rootType == TYPE_DREAM) {
if(token.windowType ! = TYPE_DREAM) { Slog.w(TAG_WM,"Attempted to add Dream window with bad token "
+ attrs.token + ". Aborting.");
returnWindowManagerGlobal.ADD_BAD_APP_TOKEN; }}else if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
if(token.windowType ! = TYPE_ACCESSIBILITY_OVERLAY) { Slog.w(TAG_WM,"Attempted to add Accessibility overlay window with bad token "
+ attrs.token + ". Aborting.");
returnWindowManagerGlobal.ADD_BAD_APP_TOKEN; }}else if (type == TYPE_TOAST) {
// Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
addToastWindowRequiresToken = doesAddToastWindowRequireToken(attrs.packageName,
callingUid, parentWindow);
if(addToastWindowRequiresToken && token.windowType ! = TYPE_TOAST) { Slog.w(TAG_WM,"Attempted to add a toast window with bad token "
+ attrs.token + ". Aborting.");
returnWindowManagerGlobal.ADD_BAD_APP_TOKEN; }}else if (type == TYPE_QS_DIALOG) {
if(token.windowType ! = TYPE_QS_DIALOG) { Slog.w(TAG_WM,"Attempted to add QS dialog window with bad token "
+ attrs.token + ". Aborting.");
returnWindowManagerGlobal.ADD_BAD_APP_TOKEN; }}else if(token.asAppWindowToken() ! =null) {
Slog.w(TAG_WM, "Non-null appWindowToken for system window of rootType=" + rootType);
// It is not valid to use an app token with other system types; we will
// instead make a new token for it (as if null had been passed in for the token).
attrs.token = null;
token = new WindowToken(this, client.asBinder(), type, false, displayContent,
session.mCanAddInternalSystemWindow);
}
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid,
session.mCanAddInternalSystemWindow);
if (win.mDeathRecipient == null) {
// Client has apparently died, so there is no reason to
// continue.
Slog.w(TAG_WM, "Adding window client " + client.asBinder()
+ " that is dead, aborting.");
return WindowManagerGlobal.ADD_APP_EXITING;
}
if (win.getDisplayContent() == null) {
Slog.w(TAG_WM, "Adding window to Display that has been removed.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
displayPolicy.adjustWindowParamsLw(win, win.mAttrs, Binder.getCallingPid(),
Binder.getCallingUid());
win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
res = displayPolicy.prepareAddWindowLw(win, attrs);
if(res ! = WindowManagerGlobal.ADD_OKAY) {return res;
}
final booleanopenInputChannels = (outInputChannel ! =null
&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
win.openInputChannel(outInputChannel);
}
/ /...
if (reportNewConfig) {
sendNewConfiguration(displayId);
}
Binder.restoreCallingIdentity(origId);
return res;
}
Copy the code
The code is very long and the focus is on the error we reported. One is the type and the other is the token. Let’s look at the type first, because the token value is related to the type.
final inttype = attrs.type; Attrs is attrs of type LayoutParamsCopy the code
Why 2? Look at the code:
Window.java
private final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();
WindowManager.java
public LayoutParams(a) {
super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
type = TYPE_APPLICATION;
format = PixelFormat.OPAQUE;
}
Copy the code
Type = TYPE_APPLICATION, TYPE_APPLICATION=2. When type =2 parentWindow=null, the token is derived from attrs.token.
WindowToken token = displayContent.getWindowToken(hasParent ? parentWindow.mAttrs.token : attrs.token);
Copy the code
Attrs is LayoutParams again. Let’s look at attrs and see where the token comes from. We need to go back to the Windows ManagerGlobal addView method. After many times of debugging, we found the place where the token was assigned.
Performed parentWindow. AdjustLayoutParamsForSubWindow (wparams) after the token is assigned.
Activity parentWindow is valid if passed, Application parentWindow is null
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) {
if (wp.token == null) {
View decor = peekDecorView();
if(decor ! =null) { wp.token = decor.getWindowToken(); }}if (curTitle == null || curTitle.length() == 0) {
final StringBuilder title = new StringBuilder(32);
if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA) {
title.append("Media");
} else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY) {
title.append("MediaOvr");
} else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
title.append("Panel");
} else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL) {
title.append("SubPanel");
} else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL) {
title.append("AboveSubPanel");
} else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG) {
title.append("AtchDlg");
} else {
title.append(wp.type);
}
if(mAppName ! =null) {
title.append(":").append(mAppName); } wp.setTitle(title); }}else if (wp.type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW &&
wp.type <= WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) {
// We don't set the app token to this system window because the life cycles should be
// independent. If an app creates a system window and then the app goes to the stopped
// state, the system window should not be affected (can still show and receive input
// events).
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 {
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();
}
if(mHardwareAccelerated || (mWindowAttributes.flags & FLAG_HARDWARE_ACCELERATED) ! =0) { wp.flags |= FLAG_HARDWARE_ACCELERATED; }}Copy the code
The type of the Window
The type of Window is determined by the Type property of the LayoutParams class in WindowManager. We can see that There are three main types of Windows.
-
APPLICATION_WINDOW: The value ranges from 1 to 99
-
SUB_WINDOW: The value ranges from 1000 to 1999
-
SYSTEM_WINDOW: The value ranges from 2000 to 2999
public int type;
public static final int FIRST_APPLICATION_WINDOW = 1;
public static final int TYPE_BASE_APPLICATION = 1;
public static final int TYPE_APPLICATION = 2;
public static final int TYPE_APPLICATION_STARTING = 3;
public static final int TYPE_DRAWN_APPLICATION = 4;
public static final int LAST_APPLICATION_WINDOW = 99;
public static final int FIRST_SUB_WINDOW = 1000;
public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;
public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;
public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;
@UnsupportedAppUsage
public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4;
public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;
public static final int LAST_SUB_WINDOW = 1999;
public static final int FIRST_SYSTEM_WINDOW = 2000;
public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW;
public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1;
@Deprecated
public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2;
@Deprecated
public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3;
public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4;
@Deprecated
public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5;
@Deprecated
public static final int TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6;
@Deprecated
public static final int TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7;
public static final int TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8;
public static final int TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9;
@Deprecated
public static final int TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10;
public static final int TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11;
public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;
public static final int TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13;
public static final int TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14;
@UnsupportedAppUsage
public static final int TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15;
public static final int TYPE_DRAG = FIRST_SYSTEM_WINDOW+16;
public static final int TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17;
public static final int TYPE_POINTER = FIRST_SYSTEM_WINDOW+18;
public static final int TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19;
public static final int TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20;
public static final int TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21;
public static final int TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW+22;
public static final int TYPE_DREAM = FIRST_SYSTEM_WINDOW+23;
public static final int TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24;
@UnsupportedAppUsage
public static final int TYPE_DISPLAY_OVERLAY = FIRST_SYSTEM_WINDOW+26;
public static final int TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27;
public static final int TYPE_PRIVATE_PRESENTATION = FIRST_SYSTEM_WINDOW+30;
public static final int TYPE_VOICE_INTERACTION = FIRST_SYSTEM_WINDOW+31;
public static final int TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32;
public static final int TYPE_VOICE_INTERACTION_STARTING = FIRST_SYSTEM_WINDOW+33;
public static final int TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW+34;
public static final int TYPE_QS_DIALOG = FIRST_SYSTEM_WINDOW+35;
public static final int TYPE_SCREENSHOT = FIRST_SYSTEM_WINDOW + 36;
public static final int TYPE_PRESENTATION = FIRST_SYSTEM_WINDOW + 37;
public static final int TYPE_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 38;
public static final int LAST_SYSTEM_WINDOW = 2999;
public static final int INVALID_WINDOW_TYPE = -1;
Copy the code
The type value of AlertDialog is 2, and the type of PopupWindow is 1000
ParentWindow. AdjustLayoutParamsForSubWindow (wparams) parentWindow is actually the mParentWindow WindowManagerImpl object, This is related to The WindowManager, and the WindowManager is retrieved from the context, so there is a different performance, and we will finally analyze why there is a difference. Again, let’s review the WindowManager fetch
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
if (createContextThemeWrapper) {
if (themeResId == Resources.ID_NULL) {
final TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
themeResId = outValue.resourceId;
}
mContext = new ContextThemeWrapper(context, themeResId);
} else {
mContext = context;
}
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
final Window w = new PhoneWindow(mContext);
mWindow = w;
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
w.setOnWindowSwipeDismissedCallback(() -> {
if(mCancelable) { cancel(); }}); w.setWindowManager(mWindowManager,null.null);
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
}
Copy the code
From the above code we can see that the mWindowManager is retrieved from the context and set to the PhoneWindow. How do we get WindowManager separately
MWindowManager is the WindowManager of the Activity or Application. AddView (mDecor, L);Copy the code
- Activity Context
Activity.java
@Override
public Object getSystemService(@ServiceName @NonNull String name) {
if (getBaseContext() == null) {
throw new IllegalStateException(
"System services not available to Activities before onCreate()");
}
if (WINDOW_SERVICE.equals(name)) {
return mWindowManager;
} else if (SEARCH_SERVICE.equals(name)) {
ensureSearchManager();
return mSearchManager;
}
return super.getSystemService(name);
}
Copy the code
Now, how does mWindowManager assign
Activity.java
@UnsupportedAppUsage
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) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if(info.softInputMode ! = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) { mWindow.setSoftInputMode(info.softInputMode); }if(info.uiOptions ! =0) {
mWindow.setUiOptions(info.uiOptions);
}
mUiThread = Thread.currentThread();
mMainThread = aThread;
mInstrumentation = instr;
mToken = token;
mAssistToken = assistToken;
mIdent = ident;
mApplication = application;
mIntent = intent;
mReferrer = referrer;
mComponent = intent.getComponent();
mActivityInfo = info;
mTitle = title;
mParent = parent;
mEmbeddedID = id;
mLastNonConfigurationInstances = lastNonConfigurationInstances;
if(voiceInteractor ! =null) {
if(lastNonConfigurationInstances ! =null) {
mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
} else {
mVoiceInteractor = new VoiceInteractor(voiceInteractor, this.this, Looper.myLooper()); }}// The mToken has a valuemWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) ! =0);
if(mParent ! =null) {
mWindow.setContainer(mParent.getWindow());
}
// Assign to mWindowManager
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;
mWindow.setColorMode(info.colorMode);
setAutofillOptions(application.getAutofillOptions());
setContentCaptureOptions(application.getContentCaptureOptions());
}
Copy the code
Window.java
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);
}
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
}
// Note that parentWindow is not null and token is not null
private WindowManagerImpl(Context context, Window parentWindow) {
mContext = context;
mParentWindow = parentWindow;
}
Copy the code
ActivityContext getSystemService gained the windowManager, windowManager mParentWindow is the Activity of PhoneWindow so isn’t empty.
WindowManagerImpl.java
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (display == null) {
throw new IllegalArgumentException("display must not be null");
}
if(! (paramsinstanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if(parentWindow ! =null) {
// If parentWindow is not empty, the token is assigned a value
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
// If there's no parent, then hardware acceleration for this view is
// set from the application's hardware acceleration setting.
final Context context = view.getContext();
if(context ! =null&& (context.getApplicationInfo().flags & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) ! =0) { wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; }}// ...
}
Copy the code
So now that we can basically analyze it, let’s look at Application’s
- Application Context
Application gets the WindowManager call getSystemService (ContextImpl)
Application.java -> ContextWrapper.java -> ContextImpl.java
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
Copy the code
ContextImpl’s call stack is shown below:
Here WindowManager is created, where mParentWindow is null
ApplicationContext. GetSystemService gained the windowManager windowManager mParentWindow is null
conclusion
So far the whole process has been analyzed, and we have solved the case. Each type of Context has its own mission, so be careful when you use it.