This is the fourth day of my participation in the November Gwen Challenge. Check out the details: The last Gwen Challenge 2021

preface

What is the relationship between Activity and Window, DecorView,viewRoot

Today we are going to explain, so that you in the interview, proficiently;

I. Introduction of basic concepts

1, the Activity,

  • Activities are responsible for controlling the life cycle and handling events;
  • Responsible for coordinating the addition and display of views and interacting with Windows and Views through callback methods;
  • An Activity contains a Window, which really controls the view and represents a Window.
  • Coordinate the addition and display of views and interact with Windows and Views through callbacks;

2, the Window

  • Window is the host of the view and is an abstract class;
  • The Activity actually holds a subclass of Window called PhoneWindow;
  • Window loads a DecorView into the Window via WindowManager and passes the DecorView to ViewRoot.

3, DecorView

  • The parent of the DecorView is FrameLayout, which is the root section of the Android View tree.
  • Inside the LinearLayout is a vertical LinearLayout. It has three parts: the ViewStub, the lazy loading view (ActionBar, according to the Theme setting), the title bar in the middle (according to the Theme setting, some layouts are not), and the interior bar below. The layout file set by setContentView is actually added to the contents bar;
ViewGroup content = (ViewGroup)findViewById(android.R.id.content);
ViewGroup rootView = (ViewGroup) content.getChildAt(0)
Copy the code

4, ViewRoot

  • Control View event handling and logic processing;
  • The ViewRoot subclass is the ViewRootImpl class, which is the link between Windows ManagerService and DecorView. The View’s three processes (measure, layout, and draw) are all done through ViewRoot.
  • View wroot is not part of the View tree. From the source implementation, it is neither a subclass of View, nor a View Group, but it implements the ViewParent interface, which allows it to act as the nominal parent of the View;
  • RootView inherits the Handler class to receive events and distribute them;
  • Android all touch screen events, keystroke events, interface refresh events are distributed through ViewRoot;

Create a DecorView

1, attach

The Activity’s setContentView () starts

public void setContentView(@LayoutRes int layoutResID) {

getWindow().setContentView(layoutResID);

initWindowDecorActionBar();

}
Copy the code

You can see that it’s actually given to the Window to load the view;

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) { .................................................................. mWindow = new PhoneWindow(this, window); / / create a Window object mWindow. SetWindowControllerCallback (this); mWindow.setCallback(this); Circulated to the Activity / / set the callback, click or state change events such as mWindow. SetOnWindowDismissedCallback (this); . mWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) ! = 0); / / to Windows Settings WindowManager object... }Copy the code
  • An instance of PhoneWindow is generated in the Attach method of the Activity;
  • With the Window object, load the DecorView into the Window;

2, the setContentView

Public void setContentView(int layoutResID) {if (mContentParent == null) {//mContentParent == null, Create a DecroView installDecor(); } else { mContentParent.removeAllViews(); Mlayoutinflater.inflate (layoutResID, mContentParent); mLayOutinflater.inflate (layoutResID, mContentParent); mlayOutinflater.inflate (layoutResID, mContentParent); Final Callback cb = getCallback(); // Add a child View to the mContentParent layout file. if (cb ! = null && ! isDestroyed()) { cb.onContentChanged(); // Callback notification, content changed}}Copy the code
  • MContentParent is the FrameLayout of the ContentView;
  • The flow of an Activity’s setContentView can be summarised as follows:
  • The Activity first generates an instance of PhoneWindow in the Attach method;
  • In the setContentView, I hand it to the Window to load the view, and I create a DecroView in the PhoneWindow;
  • During the creation process, different layout formats may be loaded according to the Theme, that is, the layout set in the Activity;

3, installDecor

private void installDecor() { if (mDecor == null) { mDecor = generateDecor(); / / generated DecorView mDecor. SetDescendantFocusability (ViewGroup. FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); if (! mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures ! = 0) { mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); } } if (mContentParent == null) { mContentParent = generateLayout(mDecor); // Format the layout for the DecorView and return mContentParent... } } } protected DecorView generateDecor() { return new DecorView(getContext(), -1); }Copy the code
  • Very simply, create a DecorView;
  • Then look at generateLayout;

4, generateLayout

Protected ViewGroup generateLayout(DecorView decor) {// Get style information from theme files TypedArray a = getWindowStyle(); . 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); }... // According to the theme style, load window layout int layoutResource; int features = getLocalFeatures(); // System.out.println("Features: 0x" + Integer.toHexString(features)); if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) ! = 0) { layoutResource = R.layout.screen_swipe_dismiss; } else if(...) {... } View in = mLayoutInflater.inflate(layoutResource, null); // Add a child View to the DecorView, which is the layout format mentioned in the DecorView introduction at the beginning of this article. That is just an example of loading different layouts depending on the theme style. decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); mContentRoot = (ViewGroup) in; ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); MContentParent 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 ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) ! = 0) { registerSwipeCallbacks(); } // Remaining setup -- of background and title -- that only applies // to top-level windows. ... return contentParent; }Copy the code
  • First get the style from the theme, then based on the style;
  • Load the corresponding layout into the DecorView and fetch the mContentParent from it;
  • Once you have that, you can go back to the code above and add a View, the layout of the Activity, to the mContentParent;

Display of DecorView

Set up a DecorView that is visible to the user after onResume via the setContentView interface. Start with ActivityThread.

Private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {attach(), Then call the activity.onCreate () and activity.onStart () life cycles, // but since it only initialized mDecor and added the layout file, it didn't add //mDecor to the PhoneWindow responsible for UI display, Activity A = performLaunchActivity(r, customIntent); . if (a ! = null) {// This executes activity.onResume () handleResumeActivity(r.token, false, R.isForward,! r.activity.mFinished && ! r.startsNotResumed); if (! r.activity.mFinished && r.startsNotResumed) { try { r.activity.mCalled = false; / / execution Activity. OnPause () mInstrumentation. CallActivityOnPause (state Richard armitage ctivity); }}}}Copy the code

Focus on handleResumeActivity(), where the DecorView is displayed and plays an important role; ViewRoot will also appear;

6, handleResumeActivity

Final void handleResumeActivity(IBinder Token, Boolean clearHide, Boolean isForward, Boolean reallyResume) { ActivityClientRecord r = performResumeActivity(Token, clearHide); ActivityClientRecord (token, clearHide); if (r ! = null) { final Activity a = r.activity; if (r.window == null && ! a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); View decor = r.window.getDecorView(); // Decor not visible to users. SetVisibility (view.invisible); ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; if (a.mVisibleFromClient) { a.mWindowAdded = true; // Was added to WindowManager, but at this time, still not visible wm.addView(decor, L); } if (! r.activity.mFinished && willBeVisible && r.activity.mDecor ! = null && ! R.h. ideForNow) {/ / here, the implementation of the important operation, allowing DecorView visible if (state Richard armitage ctivity. MVisibleFromClient) {state Richard armitage ctivity. MakeVisible (); } } } } }Copy the code

The interface is visible to us only after we execute the activity. makeVisible() method;

void makeVisible() { if (! mWindowAdded) { ViewManager wm = getWindowManager(); wm.addView(mDecor, getWindow().getAttributes()); // Add DecorView to WindowManager mWindowAdded = true; } mDecor.setVisibility(View.VISIBLE); / / DecorView visible}Copy the code
  • This DecorView is visible and displayed on the screen;
  • But among the decor,wm. AddView (mDecor, getWindow().getAttributes());
  • It plays an important role because it creates a ViewRootImpl object inside that is responsible for drawing and displaying each child View;
  • Look specifically at the addView() method, because WindowManager is an interface that Windows ManagerImpl implements;

7, addView

public final class WindowManagerImpl implements WindowManager { private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); . @Override public void addView(View view, ViewGroup.LayoutParams params) { mGlobal.addView(view, params, mDisplay, mParentWindow); }} to WindowManagerGlobal's addView() method; public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params; . synchronized (mLock) { ViewRootImpl root; // Instantiate a ViewRootImpl object root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); mViews.add(view); mRoots.add(root); mParams.add(wparams); }... Try {// Pass the DecorView to ViewRootImpl root.setView(view, wParams, panelParentView); } catch (RuntimeException e) { } }Copy the code
  • See the ViewRootImpl object instantiated and then call its setView() method;
  • The setView() method goes through a lot of trouble and finally calls the performTraversals() method.

conclusion

  • An Activity is like a controller, not responsible for the view part. The Window acts as a bearer for the internal view;
  • A DecorView is a top-level View, the outermost layout of all views;
  • ViewRoot acts as a connector for communication, notifying views through hardware awareness and interacting with users;