WMS window size calculation

This article focuses on how to confirm the size and position of each window after opening an Activity and displaying the Activity. The layout of each view in the window is not involved. An Activity contains a PhoneWindow (window), and a window represents a window.

Activity is not responsible for view control, but for controlling its life cycle and handling events. Window is the one that really controls the view. However, the addition and display of window are closely related to the life cycle.

In android system, besides the interface displayed by Activity on a window interface, there may be at least the following elements: status bar, navigation bar and input method window

1. Before the activity callback onCreate

The first time an Activity gets in touch with Window is when perfomLaunchActivity() of ActivityThread calls the attach() method of the Activity, as shown in the following figure:

Assign a value to the windowattach method, and then create a member in the windowattach method. Since PhoneWindow inherits from Window, it can be associated with WindowManager by calling setWindowManager of Window.

ActvityThread.performLaunchActivity

  /**  Core implementation of activity launch. */
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }
​
        ComponentName component = r.intent.getComponent();
        if (component == null) {
            component = r.intent.resolveActivity(
                mInitialApplication.getPackageManager());
            r.intent.setComponent(component);
        }
​
        if (r.activityInfo.targetActivity != null) {
            component = new ComponentName(r.activityInfo.packageName,
                    r.activityInfo.targetActivity);
        }
​
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }
​
        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
​
            if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
            if (localLOGV) Slog.v(
                    TAG, r + ": app=" + app
                    + ", appName=" + app.getPackageName()
                    + ", pkg=" + r.packageInfo.getPackageName()
                    + ", comp=" + r.intent.getComponent().toShortString()
                    + ", dir=" + r.packageInfo.getAppDir());
​
            if (activity != null) {
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                if (r.overrideConfig != null) {
                    config.updateFrom(r.overrideConfig);
                }
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                        + r.activityInfo.name + " with config " + config);
                Window window = null;
                if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
                    window = r.mPendingRemoveWindow;
                    r.mPendingRemoveWindow = null;
                    r.mPendingRemoveWindowManager = null;
                }
                appContext.setOuterContext(activity);
                //Call activity Attach method to establish the relationship between activity -window -windowmanager
                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);
​
                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }
                r.lastNonConfigurationInstances = null;
                checkAndBlockForNetworkAccess();
                activity.mStartedActivity = false;
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }
​
                activity.mCalled = false;
                //Set the status of the activity: onCreate
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                if (!activity.mCalled) {
                    throw new SuperNotCalledException(
                        "Activity " + r.intent.getComponent().toShortString() +
                        " did not call through to super.onCreate()");
                }
                r.activity = activity;
            }
            r.setState(ON_CREATE);
​
            mActivities.put(r.token, r);
​
        } catch (SuperNotCalledException e) {
            throw e;
​
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to start activity " + component
                    + ": " + e.toString(), e);
            }
        }
​
        return activity;
    }
    

Activity.attach

 @UnsupportedAppUsage
7703      final void attach(Context context, ActivityThread aThread,
7704              Instrumentation instr, IBinder token, int ident,
7705              Application application, Intent intent, ActivityInfo info,
7706              CharSequence title, Activity parent, String id,
7707              NonConfigurationInstances lastNonConfigurationInstances,
7708              Configuration config, String referrer, IVoiceInteractor voiceInteractor,
7709              Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
7710          attachBaseContext(context);
7711  
7712          mFragments.attachHost(null /*parent*/);
7713  
7714          mWindow = new PhoneWindow(this, window, activityConfigCallback);//Create windowphone object
7715          mWindow.setWindowControllerCallback(this);
7716          mWindow.setCallback(this);
7717          mWindow.setOnWindowDismissedCallback(this);
7718          mWindow.getLayoutInflater().setPrivateFactory(this);
7719          if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
7720              mWindow.setSoftInputMode(info.softInputMode);
7721          }
7722          if (info.uiOptions != 0) {
7723              mWindow.setUiOptions(info.uiOptions);
7724          }
             //UI thread
7725          mUiThread = Thread.currentThread();
              //Establish the relationship between window and WindowManager
7750          mWindow.setWindowManager(
7751                  (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
7752                  mToken, mComponent.flattenToString(),
7753                  (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
7754          if (mParent != null) {
7755              mWindow.setContainer(mParent.getWindow());
7756          }
7757          mWindowManager = mWindow.getWindowManager();
7758          mCurrentConfig = config;
7759  
7760          mWindow.setColorMode(info.colorMode);
7761  
7762          setAutofillOptions(application.getAutofillOptions());
7763          setContentCaptureOptions(application.getContentCaptureOptions());
7764      }

Window.setWindowManger:

frameworks/base/core/java/android/view/Window.java 
770      public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
771              boolean hardwareAccelerated) {
772          mAppToken = appToken;
773          mAppName = appName;
774          mHardwareAccelerated = hardwareAccelerated;
775          if (wm == null) {
776              wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
777          }
778          mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
779      }
​

WindowManager is an interface class. Its implementation is in WindowManagerImpl. In the important method of WindowManagerImpl, the real work is handled by WindowManagerGlobal. Both belong to the agent model. WindowManagerGlobal is used to create, manage and delete ViewRoot. The relationship between the three is as follows:

 

2 calling setContentView in activity oncreate

The perfomLaunchActivity of ActivityThread will call the onCreate method of the Activity after the attach is completed. The application will call setContentView in the onCreate callback to add a layout for the active window.

 

Activity.setContentView

​
3325      public void setContentView(@LayoutRes int layoutResID) {
3326          getWindow().setContentView(layoutResID);
3327          initWindowDecorActionBar();
3328      }

The getWindow () method here obtains the PhoneWindow object instantiated in the previous attach () method. PhoneWindow class is the specific implementation class of Window. Therefore, the next step is to call the setContentView of PhoneWindow for real operation.

PhoneWindow.setContenView

frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
 //This is where the specific contents of the window are placed. It can be mDecor or a child View of mDecor
  ViewGroup mContentParent;
422      @Override
423      public void setContentView(int layoutResID) {
424          // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
425          // decor, when theme attributes and the like are crystalized. Do not check the feature
426          // before this happens.
427          if (mContentParent == null) {
428              installDecor();//Call mContentParent for the first time, and call the installDecor method to create DecorView
429          } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
430              mContentParent.removeAllViews();//If mContentParent already exists and animation is not required, clear all view s
431          }
432  
433          if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
434              final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
435                      getContext());
436              transitionTo(newScene);
437          } else {
                //Add the layout file to mContentParent, which is assigned in installDecor
438              mLayoutInflater.inflate(layoutResID, mContentParent);
439          }
440          mContentParent.requestApplyInsets();
441          final Callback cb = getCallback();
442          if (cb != null && !isDestroyed()) {
443              cb.onContentChanged();
444          }
445          mContentParentExplicitlySet = true;
446      }

The meaning of the above code is:

First, make the first judgment. If mContentParent is empty, call installDecor(). When the mContentParent content is not empty and feature is not set_ CONTENT_ When the transitions flag bit, removeAllViews() will be called to empty the mContentParent content. Where feature_ CONTENT_ The transitions flag bit represents the transition animation of content conversion. The default is false. It can be set in the body through the attribute windowscontenttransitions.

The second if judgment is that if feature is set after mContentParent is obtained_ CONTENT_ Transitions adds Scene to start the transition. Otherwise, call mlayoutinflator Inflate (layoutresid, mContentParent) converts the layout file of the application Activity into a View tree through the LayoutInflater object and adds it to the mContentParent View.

PhoneWindow. installDecor

158      // This is the top-level view of the window, containing the window decor.
159      private DecorView mDecor;
​
2681      private void installDecor() {
2682          mForceDecorInstall = false;
2683          if (mDecor == null) {
2684              mDecor = generateDecor(-1);//1. When DecorView does not exist, call generateDecor
2685              mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
2686              mDecor.setIsRootNamespace(true);
2687              if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
2688                  mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
2689              }
2690          } else {
2691              mDecor.setWindow(this);//If decorview already exists, contact phoneindow with decorview
2692          }  
              //At this time, mDecor is not empty, but mContentParent is empty
2693          if (mContentParent == null) {
2694              mContentParent = generateLayout(mDecor);//2. Bind layout for DecorView
2695           }
.......
2814          }
2815     

 

​
2315      protected DecorView generateDecor(int featureId) {
              //Get the context of the application 
2319          Context context;
2320          if (mUseDecorContext) {
2321              Context applicationContext = getContext().getApplicationContext();
2322              if (applicationContext == null) {
2323                  context = getContext();
2324              } else {
2325                  context = new DecorContext(applicationContext, getContext());
2326                  if (mTheme != -1) {
2327                      context.setTheme(mTheme);
2328                  }
2329              }
2330          } else {
2331              context = getContext();
2332          }
2333          return new DecorView(context, featureId, this, getAttributes());
2334      }

So far, mDecorView has only been instantiated, the layout has not been loaded, and the mContentParent is empty. Call generateLayout to load the layout for decorview and instantiate mContentParent.

mContentParent = generateLayout(mDecor);//2. Bind layout for DecorView

 protected ViewGroup generateLayout(DecorView decor) {
              //Get the theme of the window and set different feature s according to different styles 
2339          TypedArray a = getWindowStyle();
2350          //Is it a floating window
2351          mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
2352          int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
2353                  & (~getForcedWindowFlags());
2354          if (mIsFloating) {
2355              setLayout(WRAP_CONTENT, WRAP_CONTENT);
2356              setFlags(0, flagsToUpdate);
2357          } else {
2358              setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
2359          }
2360         //Do you need to set the title and actionbar
2361          if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
2362              requestFeature(FEATURE_NO_TITLE);
2363          } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
2364              // Don't allow an action bar if there is no title.
2365              requestFeature(FEATURE_ACTION_BAR);
2366          }
2367         //Do you need to set actionbar overlay
2368          if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) {
2369              requestFeature(FEATURE_ACTION_BAR_OVERLAY);
2370          }
              //Whether the window is in full screen mode
2380          if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {
2381              setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
2382          }
2383          //Set the transparent attribute flag of statusbar according to style
2384          if (a.getBoolean(R.styleable.Window_windowTranslucentStatus,
2385                  false)) {
2386              setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS
2387                      & (~getForcedWindowFlags()));
2388          }
2389          //According to window_ Windowstranslucentnavigation property, set natnavigation flag
2390          if (a.getBoolean(R.styleable.Window_windowTranslucentNavigation,
2391                  false)) {
2392              setFlags(FLAG_TRANSLUCENT_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION
2393                      & (~getForcedWindowFlags()));
2394          }
2395  
2396          if (a.getBoolean(R.styleable.Window_windowOverscan, false)) {
2397              setFlags(FLAG_LAYOUT_IN_OVERSCAN, FLAG_LAYOUT_IN_OVERSCAN&(~getForcedWindowFlags()));
2398          }
2399          //Set whether to set wallpaper
2400          if (a.getBoolean(R.styleable.Window_windowShowWallpaper, false)) {
2401              setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags()));
2402          }
2403  
2404          if (a.getBoolean(R.styleable.Window_windowEnableSplitTouch,
2405                  getContext().getApplicationInfo().targetSdkVersion
2406                          >= android.os.Build.VERSION_CODES.HONEYCOMB)) {
2407              setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags()));
2408          }
2409  
             ........
2442  
2443          final Context context = getContext();
2444          final int targetSdk = context.getApplicationInfo().targetSdkVersion;
2445          final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB;
2446          final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
2447          final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP;
2448          final boolean targetPreQ = targetSdk < Build.VERSION_CODES.Q;
2449          final boolean targetHcNeedsOptions = context.getResources().getBoolean(
2450                  R.bool.target_honeycomb_needs_options_menu);
2451          final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE);
2452          //Set whether menukey is required
2453          if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) {
2454              setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE);
2455          } else {
2456              setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_FALSE);
2457          }
2458         //Set the color of statusbar
2459          if (!mForcedStatusBarColor) {
2460              mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000);
2461          }
2462          if (!mForcedNavigationBarColor) {
2463              mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000);
2464              mNavigationBarDividerColor = a.getColor(R.styleable.Window_navigationBarDividerColor,
2465                      0x00000000);
2466          }
2473  
2474          WindowManager.LayoutParams params = getAttributes();
2475  
              //Whether to draw the background of statusbar and navigationbar
2478          if (!mIsFloating) {
2479              if (!targetPreL && a.getBoolean(
2480                      R.styleable.Window_windowDrawsSystemBarBackgrounds,
2481                      false)) {
2482                  setFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
2483                          FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS & ~getForcedWindowFlags());
2484              }
2485              if (mDecor.mForceWindowDrawsBarBackgrounds) {
2486                  params.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
2487              }
2488          }
              //Do you want to draw the statusbar and navigationBar as bright themes
2489          if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) {
2490              decor.setSystemUiVisibility(
2491                      decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
2492          }
2493          if (a.getBoolean(R.styleable.Window_windowLightNavigationBar, false)) {
2494              decor.setSystemUiVisibility(
2495                      decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
2496          }
              //Whether the current mode is banged screen mode and obtain the mode of banged screen
2497          if (a.hasValue(R.styleable.Window_windowLayoutInDisplayCutoutMode)) {
2498              int mode = a.getInt(R.styleable.Window_windowLayoutInDisplayCutoutMode, -1);
2499              if (mode < LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
2500                      || mode > LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER) {
2501                  throw new UnsupportedOperationException("Unknown windowLayoutInDisplayCutoutMode: "
2502                          + a.getString(R.styleable.Window_windowLayoutInDisplayCutoutMode));
2503              }
2504              params.layoutInDisplayCutoutMode = mode;
2505          }
2506  
              //Is it currently in input mode
2516          if (!hasSoftInputMode()) {
2517              params.softInputMode = a.getInt(
2518                      R.styleable.Window_windowSoftInputMode,
2519                      params.softInputMode);
2520          }
2521          //Set the flag of the window_ DIM_ BEHIND
2522          if (a.getBoolean(R.styleable.Window_backgroundDimEnabled,
2523                  mIsFloating)) {
2524              /* All dialogs should have the window dimmed */
2525              if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) {
2526                  params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
2527              }
2528              if (!haveDimAmount()) {
2529                  params.dimAmount = a.getFloat(
2530                          android.R.styleable.Window_backgroundDimAmount, 0.5f);
2531              }
2532          }
2533          //Animate the window
2534          if (params.windowAnimations == 0) {
2535              params.windowAnimations = a.getResourceId(
2536                      R.styleable.Window_windowAnimationStyle, 0);
2537          }
2538  
2562  
2563          // Inflate the window decor.
2564          //Different layouts can be obtained according to different feature s
2565          int layoutResource;
2566          int features = getLocalFeatures();
2567          // System.out.println("Features: 0x" + Integer.toHexString(features));
2568          if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
2569              layoutResource = R.layout.screen_swipe_dismiss;
2570              setCloseOnSwipeEnabled(true);
2571          } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) 
2625          ..... }
2626          mDecor.startChanging();
              //Start another thread BackdropFrameRenderer to load related resources, including drawing statusbar and nav
2627          mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
2628          //Get ViewGroup: contentParent
2629           . . . . . 
2671          mDecor.finishChanging();
2672  
2673          return contentParent;
2674      }

The main functions of generateLayout are:

1. The first is to obtain the window theme and set different feature flag s according to different theme styles, such as whether there is TitleBar, ActionBar, float window, full screen, Progress Window, etc.

2. Next, judge different features and call onResourcesLoaded to load different layout files. For example, if the Window theme is NO_TITLE and decorview load r.layout screen_ Simple layout. This layout only contains mContentParent. At this time, mDecor is equivalent to mContentParent. If other decorative views are included, mContentParent is the child element of mDecor. Here we explain the above question: mContentParent can be mDecor or a child View of mDecor.

3. Finally, get the mContentParent and return it. From the code point of view, mContentParent is the ID in the mDecor layout_ ANDROID_ Control of content.

The relationship diagram of decorview, mContentParent and mContentRoot (equivalent to decorview) is as follows:

 

3. In the activity onresume stage, DecorView is added to the Window and displayed

 

The onCreate stage in the previous section is the creation of DecorView and mContentParent. The real display process needs to call the handlerresumeactivity method of ActivityThead.

    @Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
        //Execute application on_ Status of resume
        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
        if (r == null) {
            // We didn't actually resume the activity, so skipping any follow-up actions.
            return;
        }
​
        final Activity a = r.activity;
​
        ......
        if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();//Get phoneindow
            View decor = r.window.getDecorView();//Get DecorView
            decor.setVisibility(View.INVISIBLE);//Decorview starts to be 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 (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;
                    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;
        }
​
        .........
​
            r.activity.mVisibleFromServer = true;
            mNumVisibleActivities++;
            //When the Activity is visible, Decorview is set to visible status
            if (r.activity.mVisibleFromClient) {
                r.activity.makeVisible();
            }
        }
​
        r.nextIdle = mNewActivities;
        mNewActivities = r;
        if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r);
        Looper.myQueue().addIdleHandler(new Idler());
    

In the handleResumeActivity method of ActivityThread, we only focus on three processes

1. performResumeActivity(): performResumeActivity will call performResume of the activity, performResume will call onResume, and then enter the onResume life cycle of the activity.

2,wm.addView(): call addView of ViewManager to add DecorView and LayoutParams to the window and start the drawing process.

3,activity.makeVisible(): set DecorView visible and display DecorView

3.1 setView method of viewrootimpl

  ViewRootImpl
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;//Assign DecorView to mView
               //Monitor the change of Display. When the screen is lit or dimmed, or change the resolution
                mAttachInfo.mDisplayState = mDisplay.getState();
                mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
​
                mViewLayoutDirectionInitial = mView.getRawLayoutDirection();
                mFallbackEventHandler.setView(view);
                mWindowAttributes.copyFrom(attrs);
                if (mWindowAttributes.packageName == null) {
                    mWindowAttributes.packageName = mBasePackageName;
                }
                attrs = mWindowAttributes;
                setTag();
​
                if (DEBUG_KEEP_SCREEN_ON && (mClientWindowLayoutFlags
                        & WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) != 0
                        && (attrs.flags&WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) == 0) {
                    Slog.d(mTag, "setView: FLAG_KEEP_SCREEN_ON changed from true to false!");
                }
                // Keep track of the actual window flags supplied by the client.
                mClientWindowLayoutFlags = attrs.flags;
​
                setAccessibilityFocus(null, null);
​
​
                }
​
                boolean restore = false;
                if (mTranslator != null) {
                    mSurface.setCompatibilityTranslator(mTranslator);
                    restore = true;
                    attrs.backup();
                    mTranslator.translateWindowLayout(attrs);
                }
                if (DEBUG_LAYOUT) Log.d(mTag, "WindowLayout in setView:" + attrs);
​
                mAdded = true;
                int res; /* = WindowManagerImpl.ADD_OKAY; */
​
                // Schedule the first layout -before- adding to the window
                // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                //Request relayout and drawing interface
                requestLayout();
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                mForceDecorViewVisibility = (mWindowAttributes.privateFlags
                        & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    //Call wms to add a window
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
                } 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();
                    }
                }
                //Processing input events
                if (mInputChannel != null) {
                    if (mInputQueueCallback != null) {
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    }
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }
               ......
                // Set up the input pipeline.
                CharSequence counterSuffix = attrs.getTitle();
                mSyntheticInputStage = new SyntheticInputStage();
                InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
                InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
                        "aq:native-post-ime:" + counterSuffix);
                InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
                InputStage imeStage = new ImeInputStage(earlyPostImeStage,
                        "aq:ime:" + counterSuffix);
                InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
                InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
                        "aq:native-pre-ime:" + counterSuffix);
​
                mFirstInputStage = nativePreImeStage;
                mFirstPostImeInputStage = earlyPostImeStage;
                mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix;
            }
        }
    }
​

From the above methods, we can see that the responsibilities of ViewRootImpl are:

1) The link between WindowManager and DecorView, and more broadly, the link between Window and View. Cooperate with WindowManagerService to manage the application Window of the system.

2) Complete the drawing process of View, including measure, layout and draw.

3) Be responsible for distributing the received event events to View, such as key press, touch screen and other events.

3.2 requestLayout

 

1441      @Override
1442      public void requestLayout() {
1443          if (!mHandlingLayoutInLayoutRequest) {
1444              checkThread();//Check whether it is currently in the main thread
1445              mLayoutRequested = true;//Set mLayoutRequested
1446              scheduleTraversals();//Call scheduleTraversals
1447          }
1448      }

3.2.1 scheduleTraversals

1712      @UnsupportedAppUsage
1713      void scheduleTraversals() {
1714          if (!mTraversalScheduled) {//It will not be called multiple times in the same frame
1715              mTraversalScheduled = true;
                 //Intercept synchronous message
1716              mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
                  //The mChoreographer callback performs the drawing operation
1717              mChoreographer.postCallback(
1718                      Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
1719              if (!mUnbufferedInputDispatch) {
1720                  scheduleConsumeBatchedInput();
1721              }
1722              notifyRendererOfFramePending();
1723              pokeDrawLockIfNeeded();
1724          }
1725      }

Choreographer.choreographer called postCallback. The main function of choreographer is to coordinate the animation, input and drawing time with VSync. It receives timing pulses (such as vertical synchronization) from the display subsystem, and then arranges part of the rendering of the next frame. The general flow is shown in the following figure, which is the process of requesting VSync and receiving VSync respectively.

3.2.2 postCallback

The calling sequence of postCallback is as follows:

Choreographer.java
  private void postCallbackDelayedInternal(int callbackType,
            Object action, Object token, long delayMillis) {
        if (DEBUG_FRAMES) {
            Log.d(TAG, "PostCallback: type=" + callbackType
                    + ", action=" + action + ", token=" + token
                    + ", delayMillis=" + delayMillis);
        }

        synchronized (mLock) {
/ / current time
            final long now = SystemClock.uptimeMillis();
/ / callback execution time, which is the current time plus the delay time
            final long dueTime = now + delayMillis;
/ / obtainCallbackLocked(long dueTime, Object action, Object token) this method will pass in three parameters
/ / convert to CallbackRecord, and then add CallbackRecord to the chain according to the type of callbackQueue
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
/ / judge by time
            if (dueTime <= now) {
/ / if delayMillis is 0, execute immediately
                scheduleFrameLocked(now);
            } else {
/ / if the time is not up, send an MSG_ DO_ SCHEDULE_ The callback message is processed when the time is up. The final processing is also processed by calling / / scheduleFrameLocked
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                msg.arg1 = callbackType;
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, dueTime);
            }
        }
    }

scheduleFrameLocked

        // Enable/disable vsync for animations and drawing.
    private static final boolean USE_VSYNC = SystemProperties.getBoolean(
            "debug.choreographer.vsync", true);
     
    private void scheduleFrameLocked(long now) {
        if (!mFrameScheduled) {
            mFrameScheduled = true;
            //Determine whether VSYNC is used. This value depends on the system properties
            if (USE_VSYNC) {
                if (DEBUG_FRAMES) {
                    Log.d(TAG, "Scheduling next frame on vsync.");
                }
​
                // If running on the Looper thread, then schedule the vsync immediately,
                // otherwise post a message to schedule the vsync from the UI thread
                // as soon as possible.
                if (isRunningOnLooperThreadLocked()) {
                    //The request for Vsync signal will eventually be called to the native layer. After the processing of the native layer is completed, the onVsync of / / FrameDisplayEventReceiver will be triggered, and the callback will eventually be called to void doFrame(long frameTimeNanos, int frame). Here, the subsequent callback will not be executed until the Vsync signal is requested
                    scheduleVsyncLocked();
                } else {
                    //Send an MSG directly in the UI thread_ DO_ SCHEDULE_ The Vsync message finally calls scheduleVsyncLocked
                    Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtFrontOfQueue(msg);
                }
            } else {
                final long nextFrameTime = Math.max(
                        mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
                if (DEBUG_FRAMES) {
                    Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
                }
                //If VSYNC is not used, send MSG_ DO_ The frame message will eventually call the void doFrame(long frameTimeNanos, int frame) method
                Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, nextFrameTime);
            }
        }

3.2.2.2 process of requesting Vysnc

Next, let's take a look at the process of requesting Vysnc

   Choreographer.java
    private void scheduleVsyncLocked() {
        mDisplayEventReceiver.scheduleVsync();
    }
    
DisplayEventReceiver.java
    public void scheduleVsync() {
        if (mReceiverPtr == 0) {
            Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
                    + "receiver has already been disposed.");
        } else {
            //Call the native method through JNI
            nativeScheduleVsync(mReceiverPtr);
        }
    }

mDisplayEventReceiver corresponds to FrameDisplayEventReceiver, which inherits from DisplayEventReceiver and is mainly used to receive synchronization pulse signal Vsync The schedulevsync () method registers with the SurfaceFlinger service through the underlying nativeScheduleVsync, that is, after the next pulse is received, it will call the dispatchVsync() method of DisplayEventReceiver, which is similar to the reader prevention mode, but the method of nativeScheduleVsync calls dispatchVsync every time and only once.

Note: SurfaceFlinger is involved here, and the premise for the application to establish a connection with SurfaceFlinger is to create SurfaceSession. The last analysis of SurfaceSession was created in addwindow. Therefore, although the requestLayout method is called first, the Vsync signal cannot be received at this time. Callbacks cannot be called, so mwindowsession will be executed first Addtodisplay related logic. After receiving Vsync, continue to execute the following logic.

3.2.2.3 VSync signal received

The logical sequence of signals received from VSync is:

 

When the bottom layer sends VSYNC signal to the application, the java layer receives it through dispatchVsync(), and finally calls back onVsync in FrameDisplayEventReceiver

  private final class FrameDisplayEventReceiver extends DisplayEventReceiver
            implements Runnable {
        private boolean mHavePendingVsync;
        private long mTimestampNanos;
        private int mFrame;
​
        @Override
        public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
  //Automatically ignore VSYNC displayed on the default screen
            if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
                Log.d(TAG, "Received vsync from secondary display, but we don't support "
                        + "this case yet.  Choreographer needs a way to explicitly request "
                        + "vsync for a specific display to ensure it doesn't lose track "
                        + "of its scheduled vsync.");
                scheduleVsync();
                return;
            }
.......
​
            mTimestampNanos = timestampNanos;
            mFrame = frame;
            //The callback of this message is the current object framedisbayeventreceiver
            Message msg = Message.obtain(mHandler, this);
            msg.setAsynchronous(true);
            //mHandle is FrameHandler
            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
        }
​
        @Override
        public void run() {
            mHavePendingVsync = false;
            doFrame(mTimestampNanos, mFrame);
        }
    }

It can be seen that onVsync() sends a built-in callback message to the Looper of the main thread through the FrameHandler, and the callback is framedisclayeventreceiver. When the Looper of the main thread executes the message, it calls the run method of framedisbayeventreceiver, and then calls doFrame (question: Handler message processing calls run)?, Print msg

  void doFrame(long frameTimeNanos, int frame) {
        final long startNanos;
        synchronized (mLock) {
            if (!mFrameScheduled) {
                return; //If mFrameScheduled is false, it will be returned directly
            }
​
            long intendedFrameTimeNanos = frameTimeNanos;//Originally planned frame drawing time point
            startNanos = System.nanoTime();//Save start time
            //Because Vsync event processing adopts asynchronous mode, the time between message sending and function call starting is calculated here
            final long jitterNanos = startNanos - frameTimeNanos;
            //If the thread processes the message longer than the screen refresh cycle
            if (jitterNanos >= mFrameIntervalNanos) {               
            //Calculates the number of frames missed during a function call
                final long skippedFrames = jitterNanos / mFrameIntervalNanos;
                //When the number of dropped frames exceeds 30, the corresponding log is output
                if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
                    Log.i(TAG, "Skipped " + skippedFrames + " frames!  "
                            + "The application may be doing too much work on its main thread.");
                }
                final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
                //Time interval of aligned frames
                frameTimeNanos = startNanos - lastFrameOffset;
            }
           //If frameTimeNanos is less than one screen refresh cycle, re request the vSync signal
            if (frameTimeNanos < mLastFrameTimeNanos) {
​
                scheduleVsyncLocked();
                return;
            }
.....
            mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
            mFrameScheduled = false;
            mLastFrameTimeNanos = frameTimeNanos;
        }
​
        try {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
            AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
​
            mFrameInfo.markInputHandlingStart();
            //Callback callback respectively_ INPUT,CALLBACK_ANIMATION,CALLBACK_TRAVERSAL,CALLBACK_COMMIT,
          doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
​
            mFrameInfo.markAnimationsStart();
            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
​
            mFrameInfo.markPerformTraversalsStart();
            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
​
            doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
        } finally {
            AnimationUtils.unlockAnimationClock();
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        ......
    }

When the Vysnc event arrives, the callbacks registered in the CallbackQueue queue corresponding to the four events are executed in sequence

  void doCallbacks(int callbackType, long frameTimeNanos) {
        CallbackRecord callbacks;
        synchronized (mLock) {
            final long now = System.nanoTime();
            //Finds the callbacks whose execution time has expired from the CallbackQueues queue of the specified type
            callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
                    now / TimeUtils.NANOS_PER_MS);
            if (callbacks == null) {
                return;
            }
            mCallbacksRunning = true;
.......
        try {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
            //Since the callbackQueue is sorted in order, the CallbackRecord that arrives at all times is traversed and executed
            for (CallbackRecord c = callbacks; c != null; c = c.next) {
                c.run(frameTimeNanos);
            }
        } finally {
            synchronized (mLock) {
                mCallbacksRunning = false;
                do {
                    final CallbackRecord next = callbacks.next;
                    recycleCallbackLocked(callbacks);
                    callbacks = next;
                } while (callbacks != null);
            }
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

Execute the run method of callbacks in chronological order

    private static final class CallbackRecord {
        public CallbackRecord next;
        public long dueTime;
        public Object action; // Runnable or FrameCallback
        public Object token;
​
        public void run(long frameTimeNanos) {
            if (token == FRAME_CALLBACK_TOKEN) {
                ((FrameCallback)action).doFrame(frameTimeNanos);
            } else {
                ((Runnable)action).run();
            }
        }
    }

Then go back to viewRootImpl and call scheduleTraversals

   void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
     ......
    }
}

See that the corresponding runnable is mTraversalRunnable

ViewRootImpl.java
final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

The run method is called, so doTraversal is executed. In doTraversal, performTraversals is called to start the three processes of measurement, layout and drawing of the View.

    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
​
            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }
​
            performTraversals();
​
            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }
​

To summarize briefly, call the postCallback method of Choreographer:

1. Firstly, Choreographer supports four types of events: input, drawing, animation and submission

2. After calling the postCallback method, register the required synchronization Vsync with the SF and wait for the callback

3. After the Choreographer listens to the Vsync signal of the underlying layer, once it receives the callback signal, it will uniformly callback the four types of events of the java layer through the doFrame

4 .Log analysis

 

 

Posted by avatar.alex on Mon, 23 May 2022 22:22:09 +0300