Third, the mechanism of Window creation and Android Window Manager. (source code version SDK31)

Posted by norpel on Sun, 20 Feb 2022 11:27:59 +0100

Android window mechanism SDK31 source code analysis directory

In the previous chapter, we learned about some column processes that occur after calling setContentView method. We can see that Window objects have been held in the Activity, so how are they related?

This chapter describes in detail.

First, several classes and interfaces are introduced

ViewManager

public interface ViewManager{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

ViewManager is a set of interfaces that allow you to add and delete child views to an Activity.

For example, our common ViewGroup implements ViewManager, and the following WindowManager also inherits ViewManager.

WindowManager

//The interface that the application uses to talk to the window manager. Use context Getsystemservice (context. Window_service) gets one of them.
public interface WindowManager extends ViewManager {
    //If the LayoutParams of addView is invalid, it will be thrown, or if the first View is not removed when adding a second View, it will be thrown
	public static class BadTokenException extends RuntimeException{...}
    //If a window is on a secondary display and the specified display cannot be found, it will be thrown
    public static class InvalidDisplayException extends RuntimeException{...}
    //Returns the Display managed by the current WindowManager
    public Display getDefaultDisplay();
    //It means to remove the View from the Window. Generally, it is removed when the View calls onDetachedFromWindow, that is, after it is separated from the Window
    public void removeViewImmediate(View view);
    //Layout parameters of Window
    public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable{...}
}

An interface that inherits the ViewManager, so you can add or delete views. The final implementation class is WindowManagerImpl.

In the first chapter, we analyzed that the implementation class of WindowManager is WindowManagerImpl, but all operations are delegated to WindowManagerGlobal within WindowManagerImpl. Here we repeat it to deepen our impression.

Source code analysis

OK, let's analyze the source code and see how Window, WindowManager and Activity are related.

For the source code of SDK31, the start of Activity will eventually call the execute(ClientTransaction transaction) method of TransactionExecutor, and then call the handleLaunchActivity, handleStartActivity and handleResumeActivity methods of ActivityThread in turn.

For this source code analysis, first omit other steps and directly view the source code in the handleLaunchActivity of ActivityThread.

ActivityThread
public Activity handleLaunchActivity(ActivityClientRecord r,PendingTransactionActions pendingActions, Intent customIntent) {
	...
    //Initialize WindowManager service
    WindowManagerGlobal.initialize();
    ...
    //Call performLaunchActivity
    final Activity a = performLaunchActivity(r, customIntent);
    ...
    return a;
}

After initializing WindowManagerService, performLaunchActivity is called.

ActivityThread
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    //Create context with activity
    ContextImpl appContext = createBaseContextForActivity(r);
	Activity activity = null;
    try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        //The Activity is instantiated through classLoader, and the support Activity object is created.
        activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
        ...
	try {
        //Create / get Application
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);
        //Judge whether to obtain the reserved window
        Window window = null;
        if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
            window = r.mPendingRemoveWindow;
            r.mPendingRemoveWindow = null;
            r.mPendingRemoveWindowManager = null;
        }
        //Call the attach method of the 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, r.shareableActivityToken);
        ...
        //set up themes
        int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }
        ...
        //Callback onCreate of Activity. Through the analysis in the previous chapter, we know that DecorView is built here and held by Window.
        if (r.isPersistable()) {
            mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
        } else {
            mInstrumentation.callActivityOnCreate(activity, r.state);
        }
        //Set lifecycle state
        r.setState(ON_CREATE);
    ...
    return activity;
}

The above comments are quite clear. Let's sort out what this method does. First, create the Context of the current Activity, build the Activity through ClassLoader, and then obtain the Application. Then, we pass in a series of parameters and call the attach method of the Activity, Finally, onCreate of Activity will be called and some related properties such as theme life cycle will be set.

So go to the attach method of Activity and see what you have done

Activity
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) {
 	...
    //Found, instantiate Window object
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    mWindow.setWindowControllerCallback(mWindowControllerCallback);
    //Set the CallBack callback. The CallBack obtained by getCallback at the end of the previous chapter is set at this time.
    mWindow.setCallback(this);
    mWindow.setOnWindowDismissedCallback(this);
    mWindow.getLayoutInflater().setPrivateFactory(this);
    ...
    //Set UI Thread
    mUiThread = Thread.currentThread();
    //Hold ActivityThread object
    mMainThread = aThread;
    ...
    //One key point is to set WindowManager for Window objects
    mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    if (mParent != null) {
        mWindow.setContainer(mParent.getWindow());
    }
    //Hold the WindowManager object and obtain the WindowManager object through the getWindowManager of Window
    mWindowManager = mWindow.getWindowManager();
    ...
}

To be brief, in the attach ment of the Activity, the Window object is constructed and held by the Activity, so we can obtain the PhoneWindow through the getWindow method. After that, the WindowManager is passed into the Window, and the Activity will also hold the WindowManager object, and the WindowManager object can be obtained through getWindowManager.

In the performLaunchActivity method of ActivityThread, we can see that the onCreate method will not be called back until the call of attach is completed. Therefore, when we analyze the logic in the setContentView method of onCreate, we already include the Window object. Now that the handleLaunchActivity has been analyzed, let's take a look at handleStartActivity

ActivityThread
public void handleStartActivity(ActivityClientRecord r,PendingTransactionActions pendingActions, ActivityOptions activityOptions) {
	...
    //Callback onStart method
    activity.performStart("handleStartActivity");
    r.setState(ON_START);
    ...
    // Finally, the callback goes to onRestoreInstanceState(savedInstanceState) method
    if (pendingActions.shouldRestoreInstanceState()) {
        if (r.isPersistable()) {
            if (r.state != null || r.persistentState != null) {
                mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                                                    r.persistentState);
            }
        } else if (r.state != null) {
            mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
        }
    }
    ...
}

When you see handleStartActivity, you call back the onStart method, and then call onRestoreInstanceState of the Activity to recover data.

Finally, let's look at the handleResumeActivity method.

ActivityThread
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,boolean isForward, String reason) {
    ...
    //It will determine whether to call onRestart and onResume, and update the ActivityClientRecord data
    if (!performResumeActivity(r, finalStateRequest, reason)) {
        return;
    }
    ...
    //Window assignment
    if (r.window == null && !a.mFinished && willBeVisible) {
        r.window = r.activity.getWindow();
        View decor = r.window.getDecorView();
        decor.setVisibility(View.INVISIBLE);
        ViewManager wm = a.getWindowManager();
        WindowManager.LayoutParams l = r.window.getAttributes();
        a.mDecor = decor;
        l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
        l.softInputMode |= forwardBit;
        ...
        if (a.mVisibleFromClient) {
            if (!a.mWindowAdded) {
                a.mWindowAdded = true;
                //Bind DecorView and WindowManager through addView to prepare for view display
                wm.addView(decor, l);
            }
        }
        ...
        
        r.activity.mVisibleFromServer = true;
        mNumVisibleActivities++;
        if (r.activity.mVisibleFromClient) {
            //DecorView for display
            r.activity.makeVisible();
        }
        ...
}
Activity
//If mDecor is not added to WindowManager, add it again and display DecorView
void makeVisible() {
    if (!mWindowAdded) {
        ViewManager wm = getWindowManager();
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);
}

In handleResumeActivity, first call onRestart and onResume, then assign values to the data of window and decorview, and then bind and manage decorview through addView of WindowManager. Then display the decorview.

summary

Instantiate the activity in handleLaunchActivity, and then initialize WindowManager, Context, etc. window holds WindowManager and binds window object to the activity. Then call to setContentView in onCreate and create DecorView to be held by Window. At the same time, our own layout is included in decorview; After that, we call the handleStartActivity method, which calls onStart and onRestoreInstanceState. After calling handleResumeActivity, callback onResume is called, then DecorView is bound to WindowManager through WindowManager's addView method and WindowManager. Finally, makeVisible of Activity is displayed.

OK, so what exactly did the addView of WindowManager do? mDecor. How exactly is setvisibility displayed? See the next chapter for details: the creation of ViewRootImpl and the real loading of the view.

It's not easy to create. If you can help, you can use one key three times 🙆‍♀️. Welcome to technical discussion!

Topics: Java Android UI