English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Preface
For Android developers, View is undoubtedly a frequently encountered component during development, including its event dispatching mechanism, measurement, layout, and drawing process. If you want to customize a View, you should have a good understanding and research of the above process. This series of articles will bring you a detailed analysis of the working process of View. Before delving into the measurement, layout, and drawing processes of View, we start with Activity to see the steps to be taken from the creation of Activity to the formal operation of View. The following source code is all from Android API 21。
Let's start with setContentView
Generally, in Activity, we will write the following sentence in the onCreate() method:
setContentView(R.layout.main);
It is obvious that this is to set a main.xml layout defined by us for the activity. Let's trace the source code to see how this method works, Activity#setContentView:
public void setContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID); //Invoke the getWindow method, return mWindow initWindowDecorActionBar(); } ... public Window getWindow() { return mWindow; }
As can be seen from above, it calls the setContentView method of mWindow, then what is this 'mWindow'? Let's trace the source code and find that mWindow is of type Window, but it is an abstract class, setContentView is also an abstract method, so we need to find the implementation class of the Window class. We look for where mWindow is assigned in Activity, and we can find the following implementation in the Activity#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) { ... mWindow = new PhoneWindow(this); ... }
Let's focus on the key part, here the PhoneWindow class is instantiated, from which we know that PhoneWindow is an implementation class of Window. Then we find its setContentView method in the PhoneWindow class and see what it implements, PhoneWindow#setContentView:
@Override public void setContentView(int layoutResID) { // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window // decor, when theme attributes and the like are crystallized. Do not check the feature // before this happens. if (mContentParent == null) { // 1 installDecor(); } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); } if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, getContext()); transitionTo(newScene); } mLayoutInflater.inflate(layoutResID, mContentParent); // 2 } final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } }
Firstly, it judges whether mContentParent is null, if it is empty, then execute the installDecor() method, so what is this mContentParent? Let's take a look at its comments:
// This is the view in which the window contents are placed. It is either // This is the view in which the window contents are placed. It is either mDecor itself, or a child of mDecor where the contents go.
private ViewGroup mContentParent;
First, let's organize the content mentioned above: Activity sets the layout through the PhoneWindow's setContentView method, and before setting the layout, it will first check if mContentParent exists, and the layout file we set is a child element of mContentParent. It is a ViewGroup type, combined with the ② code, we can know that this mContentParent is the parent layout of the layout we set (i.e., main.xml). The comment also mentioned that this mContentParent is either mDecor itself or a child element of mDecor, what does this mean? Let's leave a question here, which will be explained later.
Create DecorView
Following the above-mentioned installDecor() method, let's take a look at its source code, PhoneWindow#installDecor:
private void installDecor() { if (mDecor == null) { mDecor = generateDecor(); // 1 mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) { mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); } } if (mContentParent == null) { mContentParent = generateLayout(mDecor); // 2 ... } } }
Firstly, the ① code will be executed, calling the PhoneWindow#generateDecor method:
protected DecorView generateDecor() { return new DecorView(getContext(), -1); }
It can be seen that here DecorView is instantiated, and DecorView is an inner class of PhoneWindow, inheriting from FrameLayout, so it is also a ViewGroup.
Then, what role does DecroView play?
In fact, DecorView is the topmost View in the entire ViewTree, it is a FrameLayout layout that represents the entire interface of the application. Below this layout, there are title view and content view as two child elements, and the content view is the mContentParent mentioned above. Let's take a look at the ② code, PhoneWindow#generateLayout method
protected ViewGroup generateLayout(DecorView decor) { // Apply data from the current theme. // Get style information from the theme file 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); } if(...){ ... } // Inflate the window decor. // Load window layout int layoutResource; int features = getLocalFeatures(); // System.out.println("Features: 0x" + Integer.toHexString(features)); if ((features & (1 { if ((features & (<< FEATURE_SWIPE_TO_DISMISS)) != 0) { layoutResource = R.layout.screen_swipe_dismiss; } else if(...){ ... } View in = mLayoutInflater.inflate(layoutResource, null); //Load layoutResource decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); //Add child View to DecorView, i.e., mContentParent mContentRoot = (ViewGroup) in; ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); // Here, it is getting mContentParent if (contentParent == null) { throw new RuntimeException("Window couldn't find content container view"); } if ((features & (1 { if ((features & (<< FEATURE_INDETERMINATE_PROGRESS)) != 0) { ProgressBar progress = getCircularProgressBar(false); if (progress != null) { progress.setIndeterminate(true); } } if ((features & (1 { if ((features & (<< FEATURE_SWIPE_TO_DISMISS)) != 0) { registerSwipeCallbacks(); } // Remaining setup -- of background and title -- that only applies // to top-level windows. ... return contentParent; }
As can be seen from the above code, this method still does a considerable amount of work. First, it sets the style of DecorView based on the theme style set, such as whether there is a titlebar or not, and then adds a child View to DecorView. Here, the child View is the mContentParent mentioned above. If FEATURE_NO_ACTIONBAR is set above, DecorView will only have one child View, which also explains the above question: mContentParent is either DecorView itself or a child element of DecorView.
The structure of DecorView can be represented by the following diagram:
Summary:DecorView is the top-level View, which internally has two child elements: titlebar and contentParent. The id of contentParent is content, and the main.xml layout we set is a child element inside contentParent.
After DecorView is created, let's go back to the setContentView method of PhoneWindow, and take a look at the code marked with number ②: mLayoutInflater.inflate(layoutResID, mContentParent); This loads the main.xml layout file we set and sets mContentParent as the parent layout of main.xml. As for how it is loaded, it will not be expanded here.
So far, through the setContentView method, DecorView has been created and the layout we provided has been loaded, but at this point, our View is still invisible because we have only loaded the layout without performing any measurement, layout, or drawing work on the View. Before the measurement process of View, there is a step that needs to be done, which is to add DecorView to the window, and then a series of processes are triggered to invoke the ViewRootImpl#performTraversals method, which will officially start the three major processes of measurement, layout, and drawing within this method. As for how this series of processes works, because it involves many mechanisms, a brief explanation is given here:
Add DecorView to Window
Each Activity component has an associated Window object, which is used to describe an application window. Each application window internally contains a View object, which is used to describe the view of the application window. The previous section analyzed the process of creating DecorView, and now we need to add DecorView to the Window object. To understand this process, we first need to briefly understand the creation process of Activity:
Firstly, start the Activity in ActivityThread#handleLaunchActivity, where the Activity#onCreate method will be called to complete the creation of the DecorView mentioned above. After the onCreate() method is executed, the handleLaunchActivity method will continue to call ActivityThread#handleResumeActivity method. Let's take a look at the source code of this method:
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) { //... ActivityClientRecord r = performResumeActivity(token, clearHide); // Here, the onResume() method will be called if (r != null) { final Activity a = r.activity; //... if (r.window == null && !a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); // Obtain the window object View decor = r.window.getDecorView(); // Obtain the DecorView object decor.setVisibility(View.INVISIBLE); ViewManager wm = a.getWindowManager(); // Obtain the WindowManager object WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; if (a.mVisibleFromClient) { a.mWindowAdded = true; wm.addView(decor, l); // Call the addView method } //... } } }
Inside this method, it gets the window object associated with the activity, the DecorView object, and the WindowManager object, and WindowManager is an abstract class, its implementation class is WindowManagerImpl, so the subsequent call is to the WindowManagerImpl#addView method, let's look at the source code:
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); } }
Then it called the member function of mGlobal, and mGlobal is an instance of WindowManagerGlobal, so let's see the WindowManagerGlobal#addView method:
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ... ViewRootImpl root; View panelParentView = null; synchronized (mLock) { ... root = new ViewRootImpl(view.getContext(), display); // 1 view.setLayoutParams(wparams); mViews.add(view); mRoots.add(root); mParams.add(wparams); } // Do this last because it fires off messages to start doing things try { root.setView(view, wparams, panelParentView); // 2 } // BadTokenException or InvalidDisplayException, clean up. synchronized (mLock) { final int index = findViewLocked(view, false); if (index >= 0) { removeViewLocked(index, true); } } throw e; } }
First, look at the ① code location, where the ViewRootImpl class is instantiated, then, at the ② code location, the ViewRootImpl#setView method is called, and DecorView is passed in as a parameter. Inside this method, a call is initiated to the WMS (WindowManagerService) in a cross-process manner, thus adding DecorView to the Window. During this process, ViewRootImpl, DecorView, and WMS will be associated with each other. The detailed process will not be expanded here.
Finally, through the WMS, the ViewRootImpl#performTraversals method is called to start the measurement, layout, and drawing process of the View. These three processes will be detailed in the following text, thank you for reading.
Declaration: The content of this article is from the Internet, and the copyright belongs to the original author. The content is contributed and uploaded by Internet users spontaneously. This website does not own the copyright, has not been edited by humans, and does not assume any relevant legal liability. If you find any content suspected of copyright infringement, please send an email to: notice#oldtoolbag.com (When sending an email, please replace # with @ to report, and provide relevant evidence. Once verified, this site will immediately delete the suspected infringing content.)