Refer to the article

  1. Preliminary understanding of Window system – Simple book
  2. AMS – Activity management Activity start and destroy process – simple book

What is a Window

Window, as its name implies, means window. In Android, the final drawing of graphics is done through Window. The Window is the View manager. Whether it’s an Activity,Toast, or Dialog, it ends up attaching its View to the Window.

Window creation process

Window creation in the Activity

The onCreate method is not the first to be called in the Activity lifecycle. The first method actually called is the attach method. In the ActivityThread. Java# performLaunchActivity method, the acitivty instance is created by calling attach and then onCreate.

The code in ActivityThread.java is as follows (note: the code below has been truncated to preserve only the rough flow code)

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ActivityInfo aInfo = r.activityInfo; . ContextImpl appContext = createBaseContextForActivity(r); Activity activity =mInstrumentation.newActivity( cl, component.getClassName(), r.intent); Application app = r.packageInfo.makeApplication(false, mInstrumentation);
        Configuration config = new Configuration(mCompatConfiguration);

        appContext.setOuterContext(activity);
        activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window, r.configCallback, r.assistToken); .if (r.isPersistable()) {
            // The system can save data after shutdown.
            The Acitivty property persistableMode needs to be configured in the manifest file
            mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
        } else {// This is where you gomInstrumentation.callActivityOnCreate(activity, r.state); }...return activity;
    }
Copy the code

The internal flow of the callActivityOnCreate method is callActivityOnCreate->Activity#performCreate->Activity#onCreate. Next, let’s look at the attach method

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) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);
		/ / activityConfigCallback listens for configuration change, this parameter was introduced to ViewRootImpl# setActivityConfigCallback
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        // called when the status bar, picture in picture, etc., mode changes
        mWindow.setWindowControllerCallback(this); 
        // This is passed in as window.callback
        // Interface related callback calls, window property changes, contentView changes, etc
        // And event distribution
        mWindow.setCallback(this);
        // Call onWindowDismissed when the window disappears. Finish is called in the Activity
        mWindow.setOnWindowDismissedCallback(this);
        // Set the constructor for the root View, Activity#onCreateView
        mWindow.getLayoutInflater().setPrivateFactory(this);
        // Set the input mode display status to unspecified
        if(info.softInputMode ! = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) { mWindow.setSoftInputMode(info.softInputMode); }// I don't know what it is
        if(info.uiOptions ! =0) {
            mWindow.setUiOptions(info.uiOptions);
        }
        // Get the current UI thread
        mUiThread = Thread.currentThread();
        // mUiThread = mMainThread
        mMainThread = aThread;
        / / structure
        mInstrumentation = instr;
        /* Token is a binder proxy, a token class inherited from iApplicationToken. Stub, which identifies an activity token with only one method, getName */
        mToken = token;
        // Ident is 0
        mIdent = ident;
        // The current application object
        mApplication = application;
        // Incoming intent
        mIntent = intent;
        // Caller information
        mReferrer = referrer;
        mComponent = intent.getComponent();
        mActivityInfo = info;
        // Default title
        mTitle = title;
        // Null (Fragment, deprecated)
        mParent = parent;
        // Do not know the effect
        mEmbeddedID = id;
        / / I don't know
        mLastNonConfigurationInstances = lastNonConfigurationInstances;
        // mVoiceInteractor voice interaction
        if(voiceInteractor ! =null) {
            if(lastNonConfigurationInstances ! =null) {
                mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
            } else {
                mVoiceInteractor = new VoiceInteractor(voiceInteractor, this.this, Looper.myLooper()); }}// setWindowManager passes in WindowManger
        // WindowManager is an interface whose implementation is WindowManagerImp
        // WindowMangerImpl is a proxy class, which ultimately performs various window operations through WindowMnagerGlobal. Windows ManagerGlobal is an in-process singleton class.
        // FLAG_HARDWARE_ACCELERATED Determines whether hardware acceleration is enabled for your Activity
        // For acceleration, refer to the articlemWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) ! =0);
        // If there is mParent, the DecorView of the current Activity window is not treated as the top-level window, but as the parent container determines how it should be displayed
        // Otherwise, the Activity's DecorView will act as the top-level Window
        if(mParent ! =null) {
            mWindow.setContainer(mParent.getWindow());
        }
        // Get the WindowManagerImpl object corresponding to Acitivty
        mWindowManager = mWindow.getWindowManager();
        // The current configuration, such as vertical and horizontal direction, etc
        mCurrentConfig = config;
        / / set the bitmap mode, specific refer to https://developer.android.google.cn/training/wide-color-gamut?hl=zh-cn
        mWindow.setColorMode(info.colorMode);
		
        setAutofillCompatibilityEnabled(application.isAutofillCompatibilityEnabled());
        enableAutofillCompatibilityIfNeeded();
    }
Copy the code

First of all, you can see that in attach method you just new a PhoneWindow instance,

mWindow = new PhoneWindow(this, window, activityConfigCallback);
Copy the code

The specific PhoneWindow is created as follows

/** * Constructor for main window of an activity. */
    public PhoneWindow(Context context, Window preservedWindow, ActivityConfigCallback activityConfigCallback) {
        this(context);
        // Only main activity windows use decor context, all the other windows depend on whatever
        // context that was given to them.
        mUseDecorContext = true;
        if(preservedWindow ! =null) {
            mDecor = (DecorView) preservedWindow.getDecorView();
            mElevation = preservedWindow.getElevation();
            mLoadElevation = false;
            mForceDecorInstall = true;
            // If we're preserving window, carry over the app token from the preserved
            // window, as we'll be skipping the addView in handleResumeActivity(), and
            // the token will not be updated as for a new window.
            getAttributes().token = preservedWindow.getAttributes().token;
        }
        // Even though the device doesn't support picture-in-picture mode,
        // an user can force using it through developer options.
        boolean forceResizable = Settings.Global.getInt(context.getContentResolver(),
                DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
        mSupportsPictureInPicture = forceResizable || context.getPackageManager().hasSystemFeature(
                PackageManager.FEATURE_PICTURE_IN_PICTURE);
        mActivityConfigCallback = activityConfigCallback;
    }
Copy the code

Mwindow.setcallback (this) sets the Window Callback, which is mostly related to input events such as touch. The specific Callback method is as follows:

  public interface Callback {
      public boolean dispatchKeyEvent(KeyEvent event);
      public boolean dispatchKeyShortcutEvent(KeyEvent event);
      public boolean dispatchTouchEvent(MotionEvent event);
      public boolean dispatchTrackballEvent(MotionEvent event);
      public boolean dispatchGenericMotionEvent(MotionEvent event);
      public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event); .Copy the code

There are two points to analyze at setWindowManager

  1. getSystemServiceAcquired object
  2. setWindowManagerInternal operation of
mWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) ! =0);
Copy the code

1. GetSystemService Obtain the object

Context is passed in as a ContextImple object, and its internal implementation of getSystemService is as follows

 @Override
    public Object getSystemService(String name) {
        if (vmIncorrectContextUseEnabled()) {
          ...
        }
        return SystemServiceRegistry.getSystemService(this, name);
    }
Copy the code

In the SystemServiceRegistry class, a Windows manager impl object is statically built, So clearly the setWindowManager in the Active # Attach method is passing in a Windows Manager ImpL object.

static{
 registerService(Context.WINDOW_SERVICE, WindowManager.class,
                new CachedServiceFetcher<WindowManager>() {
            @Override
            public WindowManager createService(ContextImpl ctx) {
                return new WindowManagerImpl(ctx);
            }});
}
/** * Statically registers a system service with the context. * This method must be called during static initialization only. */
    private static <T> void registerService(@NonNull String serviceName,
            @NonNull Class<T> serviceClass, @NonNull ServiceFetcher<T> serviceFetcher) {
        SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
        SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
        SYSTEM_SERVICE_CLASS_NAMES.put(serviceName, serviceClass.getSimpleName());
    }
Copy the code

2. Internal operations of setWindowManager

The setWindowManager method creates a WindowManagerImpl object to manage the Window and uses the WindowManagerImpl instance passed in as its mParentWindow property

Obviously the WindowManagerImpl instance obtained in getSystemService is the mParentWindow property of all other Acitivty WindowManagerImpl objects.

The window.java code looks like this

 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);
    }
Copy the code

The windowManagerImp.java code looks like this:

public final class WindowManagerImpl implements WindowManager {
    @UnsupportedAppUsage
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    @VisibleForTesting
    public final Context mContext;
    private final Window mParentWindow;

    private IBinder mDefaultToken;

    public WindowManagerImpl(Context context) {
        this(context, null);
    }

    private WindowManagerImpl(Context context, Window parentWindow) {
        mContext = context;
        mParentWindow = parentWindow;
    }

    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }

    public WindowManagerImpl createPresentationWindowManager(Context displayContext) {
        return newWindowManagerImpl(displayContext, mParentWindow); }... }Copy the code

This code describes the creation of the Window in the Activity. Next, look at the process by which a View is added to the Window

The process of adding a View to a Window

The process of adding a View to a Window in an Activity

As soon as the Window is added, the onCreate method is called, and the View is added to the onCreate. In the first paragraph of the article begins with performLaunchActivity method in code can be seen, after call attach method is called after mInstrumentation. CallActivityOnCreate method, After calling the Activity#performCreate method, go to the Activity#onCreate method

Instrumentation. Java# callActivityOnCreate code:

public void callActivityOnCreate(Activity activity, Bundle icicle, PersistableBundle persistentState) {
        prePerformCreate(activity);
        activity.performCreate(icicle, persistentState);
        postPerformCreate(activity);
}
Copy the code

The code for activity.java #performCreate is as follows:

final void performCreate(Bundle icicle, PersistableBundle persistentState) {...if(persistentState ! =null) {
            onCreate(icicle, persistentState);
        } else {
            onCreate(icicle);// go to onCreate}... }Copy the code

The creation of a DecorView

First the setContentView method is called in Activity#onCreate

The active #setContentView method actually calls the Window#setContentView method

public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
}
Copy the code

GetWindow ().setContentView

PhoneWindow.java

public class PhoneWindow extends Window implements MenuBuilder.Callback {
    privateDecorView mDecor; ViewGroup mContentParent; .@Override
    public void setContentView(int layoutResID) {
        / / code 1
        if (mContentParent == null) {
            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 {
            / / code 2
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        / / code 3
        if(cb ! =null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true; }}Copy the code
  • The first thing you’ll see is code 1mContentParentWhether it isnull, such asnullThe call

MContentParent is an important property in PhoneWindow. MContentParent is the Window container for View content, which can be mDecor itself or a subview of mDecor. (mDecor is a top-level View, and DecorView inherits the FragmentLayout class)

  • Code 2 parses the passed layoutResID through the mLayoutInflater and puts it inside the mContentParent.

  • Code 3 gets the Callback object set and calls onContentChanged() to notify the View that the content has changed.

The Callback object is the Callback object set in the Activity#attach method. OnContentChanged onContentChanged() is a Callback method in the Activity This method is called when the layout of the Activity changes, that is, when the setContentView() or addContentView() methods have finished executing. For example, the findViewById() methods of various views in the Activity can be placed in this method. Of course, in general, most developers get the object instance in the onCreate method by executing setContentView.

Next, look at the installDecor method

InstallDecor implementation

private void installDecor(a) {
        mForceDecorInstall = false;
        / / code 1
        if (mDecor == null) {
            mDecor = generateDecor(-1);
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if(! mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures ! =0) { mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); }}else {
            mDecor.setWindow(this);
        }
        / / code 2
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);

            // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
            mDecor.makeFrameworkOptionalFitsSystemWindows();

            final DecorContentParent decorContentParent = 
            		(DecorContentParent) mDecor.findViewById(R.id.decor_content_parent);

            if(decorContentParent ! =null) { mDecorContentParent = decorContentParent; mDecorContentParent.setWindowCallback(getCallback()); . }else{ mTitleView = findViewById(R.id.title); . }... }}Copy the code
  • At code 1, callgenerateDecorMethod to generate amDecorObject,DecorViewWill be called in the constructor ofsetWindowMethods,PhoneWindowObject passed in.

GenerateDecor method

  • This is called in code 2generateLayoutgeneratemContentParentobject

The generateDecor code below doesn’t have much to say

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, this);
                if(mTheme ! = -1) {
                    context.setTheme(mTheme);// set the theme. The theme set by AndroidManifest is assigned to ActivityThread#performLaunchActivity}}}else {//
            context = getContext();
        }
        return new DecorView(context, featureId, this, getAttributes());
    }
Copy the code

GenerateLayout code is as follows

protected ViewGroup generateLayout(DecorView decor) {

        // Set the theme according to the current theme
        TypedArray a = getWindowStyle();
        // Check if it is a suspension window
        mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false); .// Determine whether to add actionBar and title according to theme
        if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
            requestFeature(FEATURE_NO_TITLE);
        } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
            // Don't allow an action bar if there is no title.requestFeature(FEATURE_ACTION_BAR); . WindowManager.LayoutParams params = getAttributes();// Whether to draw the status bar and navigation bar background
        if(! mIsFloating) {if(! targetPreL && a.getBoolean( R.styleable.Window_windowDrawsSystemBarBackgrounds,false)) {
                setFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
                        FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS & ~getForcedWindowFlags());
            }
            if(mDecor.mForceWindowDrawsBarBackgrounds) { params.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; }}// Whether to draw StatusBar and navigationBar as bright themes
        if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) {
            decor.setSystemUiVisibility(
                    decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
        }
        if (a.getBoolean(R.styleable.Window_windowLightNavigationBar, false)) {
            decor.setSystemUiVisibility(
                    decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
        }
        // Whether the current mode is banged to get the banged mode
        if (a.hasValue(R.styleable.Window_windowLayoutInDisplayCutoutMode)) {
            int mode = a.getInt(R.styleable.Window_windowLayoutInDisplayCutoutMode, -1);
            if (mode < LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
                    || mode > LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
                throw new UnsupportedOperationException("Unknown windowLayoutInDisplayCutoutMode: "+ a.getString(R.styleable.Window_windowLayoutInDisplayCutoutMode)); } params.layoutInDisplayCutoutMode = mode; }...// Is the current input mode
        if(! hasSoftInputMode()) { params.softInputMode = a.getInt( R.styleable.Window_windowSoftInputMode, params.softInputMode); }// Window animation
        if (params.windowAnimations == 0) {
            params.windowAnimations = a.getResourceId(
                    R.styleable.Window_windowAnimationStyle, 0);
        }

        // The rest are only done if this window is not embedded; otherwise,
        // the values are inherited from our container.
        if (getContainer() == null) {
            if (mBackgroundDrawable == null) {

                if (mFrameResource == 0) {
                    mFrameResource = a.getResourceId(R.styleable.Window_windowFrame, 0);
                }

                if(a.hasValue(R.styleable.Window_windowBackground)) { mBackgroundDrawable = a.getDrawable(R.styleable.Window_windowBackground); }}if (a.hasValue(R.styleable.Window_windowBackgroundFallback)) {
                mBackgroundFallbackDrawable =
                        a.getDrawable(R.styleable.Window_windowBackgroundFallback);
            }
            if (mLoadElevation) {
                mElevation = a.getDimension(R.styleable.Window_windowElevation, 0);
            }
            mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false);
            mTextColor = a.getColor(R.styleable.Window_textColor, Color.TRANSPARENT);
        }

        // Inflate the window decor.

        int layoutResource;
        int features = getLocalFeatures();
        / / code 1
        // System.out.println("Features: 0x" + Integer.toHexString(features));
        if ((features & ((1 << FEATURE_LEFT_ICON) | (1<< FEATURE_RIGHT_ICON))) ! =0) {
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogTitleIconsDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = R.layout.screen_title_icons;
            }
            // XXX Remove this once action bar supports these features.
            removeFeature(FEATURE_ACTION_BAR);
            // System.out.println("Title Icons!" );
        } else if ((features & ((1 << FEATURE_PROGRESS) | (1<< FEATURE_INDETERMINATE_PROGRESS))) ! =0
                && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
            // Special case for a window with only a progress bar (and title).
            // XXX Need to have a no-title version of embedded windows.
            layoutResource = R.layout.screen_progress;
            // System.out.println("Progress!" );
        } else if ((features & (1<< FEATURE_CUSTOM_TITLE)) ! =0) {
            // Special case for a window with a custom title.
            // If the window is floating, we need a dialog layout
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogCustomTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = R.layout.screen_custom_title;
            }
            // XXX Remove this once action bar supports these features.
            removeFeature(FEATURE_ACTION_BAR);
        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
            // If no other features and not embedded, only need a title.
            // If the window is floating, we need a dialog layout
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else if ((features & (1<< FEATURE_ACTION_BAR)) ! =0) {
                layoutResource = a.getResourceId(
                        R.styleable.Window_windowActionBarFullscreenDecorLayout,
                        R.layout.screen_action_bar);
            } else {
                layoutResource = R.layout.screen_title;
            }
            // System.out.println("Title!" );
        } else if ((features & (1<< FEATURE_ACTION_MODE_OVERLAY)) ! =0) {
            layoutResource = R.layout.screen_simple_overlay_action_mode;
        } else {
            // Embedded, so no decoration is needed.
            layoutResource = R.layout.screen_simple;
            // System.out.println("Simple!" );
        }
        mDecor.startChanging();
        / / code 2
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
        // ID_ANDROID_CONTENT = com.android.internal.R.id.content
        // findViewById is from mDecor to findView objects
        / / code 3
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }

        if ((features & (1<< FEATURE_INDETERMINATE_PROGRESS)) ! =0) {
            ProgressBar progress = getCircularProgressBar(false);
            if(progress ! =null) {
                progress.setIndeterminate(true); }}// If getContainer == null, it is a top-level View. Normally, the Activity Window corresponds to the top-level Window.
        // Set the background color and title color
        if (getContainer() == null) {
            mDecor.setWindowBackground(mBackgroundDrawable);

            final Drawable frame;
            if(mFrameResource ! =0) {
                frame = getContext().getDrawable(mFrameResource);
            } else {
                frame = null;
            }
            mDecor.setWindowFrame(frame);

            mDecor.setElevation(mElevation);
            mDecor.setClipToOutline(mClipToOutline);

            if(mTitle ! =null) {
                setTitle(mTitle);
            }

            if (mTitleColor == 0) {
                mTitleColor = mTextColor;
            }
            setTitleColor(mTitleColor);
        }

        mDecor.finishChanging();

        return contentParent;
    }
Copy the code
  • At code 1, according tofeatureAs well aswindowType to determine the introducedmContentParentLayout. Generally, the layout of full-screen display isR.layout.screen_simple.R.layout.screen_simpleThere was aandroid:id="@android:id/content"theFrameLayoutLayout (regardlesslayoutResourceThe corresponding layout file is that one, both of themandroid:id="@android:id/content"theFrameLayout).
  • At code 2, callDecorView#onResourcesLoadedPass in the ID of the layout and parse to generate oneDecorViewView object propertiesmContentRoot.
  • At code 3,findViewByIdThe implementation of theWindow.java, its final callmDecorthefindViewByIdMethod, searchmDecorOf the corresponding ID underView.ID_ANDROID_CONTENTThe value ofcom.android.internal.R.id.content. Obviously,findViewByIdTo obtain theViewGroupObject ismDecorUnder themContentRootWith id@android:id/contenttheFrameLayoutObject.

Next, code analysis. First, R.l ayout. Screen_simple (/ frameworks/base/core/res/res/layout/screen_simple XML) layout code, as shown below, this can’t.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:fitsSystemWindows="true"
     android:orientation="vertical">
     <ViewStub android:id="@+id/action_mode_bar_stub"
               android:inflatedId="@+id/action_mode_bar"
               android:layout="@layout/action_mode_bar"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:theme="? attr/actionBarTheme" />
     <FrameLayout
          android:id="@android:id/content"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:foregroundInsidePadding="false"
          android:foregroundGravity="fill_horizontal|top"
          android:foreground="? android:attr/windowContentOverlay" />
</LinearLayout>
Copy the code

Take a look at the DecorView#onResourcesLoaded code.

class DecorView{
    void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
            if(mBackdropFrameRenderer ! =null) {... }/ / code 1
            mDecorCaptionView = createDecorCaptionView(inflater);
            // at code 2
            final View root = inflater.inflate(layoutResource, null);
            if(mDecorCaptionView ! =null) {
                if (mDecorCaptionView.getParent() == null) {
                    addView(mDecorCaptionView,
                            new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
                }
                mDecorCaptionView.addView(root,
                        new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
            } else {
                / / code 3
                // Put it below the color views.
                addView(root, 0.newViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); } mContentRoot = (ViewGroup) root; initializeElevation(); }}Copy the code
  • One is created in code 1mDecorCaptionViewObject,createDecorCaptionViewAccording to its English notesFree-floating overlapping Windows require a title.We have no Free floating Windows require a captionActivitysetContentViewIn the process,mDecorCaptionView == null, so the code will go to code 3
  • Code 2 According tolayoutResource, parse the resource file and generate the correspondingView, i.e.,R.layout.screen_simple(or otherwise) analyticallyView.
  • Code 3 parses the layout filerootObject, add toDecorViewThe son ofView

Let’s look at the PhoneView#generateLayout method implementation of the findViewById method

class Window{
    @Nullable
    public View findViewById(@IdRes int id) {
        returngetDecorView().findViewById(id); }}Copy the code

FindViewById looks for a ViewGroup object with ID R.D.C. Tent from mDecor. The PhoneView mContentParent object is a child ViewGroup with id Content under the mContentRoot in the DecorView.

Let’s go back to the PhoneView#setContentView method and look at code 2

mLayoutInflater.inflate(layoutResID, mContentParent);
Copy the code

LayoutResID is the layout file id value passed in by Activity#setContentView. The layout passed in by Activity#setContentView is obviously a child View of the mContentParent.

With that said, we can now summarize an inclusion relationship for activities, PhoneWindows, and decorViews.

One: That thing with DecorView

But this is just the creation of the DecorView, the View has not been added to the Window yet!!

Add a DecorView to the PhoneView

Immediately after executing the create phase in the ActivityThread’s handleLaunchActivity, start the resume process by calling the handleResumeActivity method. The handleResumeActivity method is as follows

public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {...// Code 1 calls the Activity's onResume indirectly
        finalActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason); .if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            // Set code 2 to invisible until the DecorView is added to the Window
            decor.setVisibility(View.INVISIBLE);
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            // Code 3 sets the type of the Window. TYPE_BASE_APPLICATION indicates that the Window is an application-level Window, at the bottom of the application Window.
            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;
                    // Code 4 adds decor to Window
                    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); }}// If the window has already been added, but during resume
            // we started another activity, then don't yet make the
            // window visible.
        } else if(! willBeVisible) {if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
            r.hideForNow = true; }... }Copy the code

Code 4 calls the WindowManager addView method. The WindowManager implementation class is WindowManagerImpl, and WindowManagerImpl#addView calls the WindowManagerGlobal addView method. Windowmanagerglobar #addView is an example of an onKeyDown problem with Android 9 and Android 9.