The article has been authorized to the public account[Guo Lin]Exclusive release. Fellow colleagues do not reprint twice, thank you ~

preface

When it comes to Android views, the first thing you’ll think of is Activity and View. After all, those are the two components we’ve been exposed to most since we started. But mentioning the connection between an Activity and a View and the design context might confuse a lot of people. Another important view system concept that doesn’t get mentioned as much is Windows, which the Android designers used Demeter’s law to make it easier for developers to use. This article will from the design background as the starting point of the Activity, Window, View implementation process, to help you look at the Android View system from a different Angle, I believe that reading will make you fresh and refreshing.

directory

  • 1. Design background
    • 1.1 Significance of View
    • 1.2 How to manage complex Views?
    • 1.3 How to gracefully connect the relationship between Windows?
  • 2. Implementation process
    • 2.1 Origin of Activity
    • 2.2 PhoneWindow is not equivalent to “Window”
    • 2.3 When a DecorView is created
    • 2.4 How does ViewRootImpl coordinate the relationship between View and Window?

1. Design background

1.1 Significance of View

View literally means View and is used to display various views. The drawing process of View is divided into measure, layout and draw. The first two steps are used to determine the width, height and position. The real drawing of View is in the draw process. Inside the Canvas, JNI calls the bottom layer to realize the real drawing operation.

Now that Canvas can draw, what’s the point of View?

In order to draw colorful effects, Canvas is far from enough. It has to cooperate with Paint and Matrix, etc. Such a series of operations make it increasingly difficult to master Canvas, which is not easy, and the reuse rate is getting lower and lower. Drawing various complex interfaces is almost an impossible task. Faced with such a pain point, Android system encapsulates a component View for managing drawing through template design mode, shielding a lot of details while providing three template methods measure, layout and draw. Developers can define the width, height, position and shape of View by themselves through View’s three template methods. Solved the problem of large amount of template code and low reuse rate.

A complex interface usually contains many elements such as text, images, etc. Android encapsulates TextView and ImageView according to a single design principle. It seems that everything is fine, but it is a big project to put these views, all kinds of coordinate calculation will not be confused for a while, in fact, there are no more than a few rules, So Android uses the Layout feature of a View to encapsulate layouts such as RelativeLayout and LinearLayout to control the position relationship between views and further improve developer efficiency.

1.2 How to manage complex Views?

With custom views we can draw any effect we want, and everything looks great. When you are staring at the screen to enjoy your work, a screen pops up. After a series of analysis, you learn that other apps also control the screen through View, and you try to compete for the screen again through the same operation. When the screen goes black, you have to switch back to Symbian

Jokes aside, back to the problem. Mismanagement of the View resulted in a messy screen. Generally speaking, when users operate an app, they don’t want other apps to pop up, so a mechanism to manage the complexity of the View is urgently needed in this context. So Android creates a system service WindowManagerService(WMS) in the system process to manage the window on the screen, and the View can only be displayed in the corresponding window, if not in accordance with the regulation does not open the window and the corresponding View can not be displayed

Why does WMS need to run in a system process?

Since each app has a process, to manage all application processes, WMS needs to find a suitable place to override all application processes, and system processes are the most suitable choice

1.3 How to gracefully connect the relationship between Windows?

Custom View can customize a variety of View effects, Windows can let the View orderly display, everything is better. However, there are many interfaces (Windows) in each App. Controlling Windows and views by window /View alone will face a fatal problem: “No jump and rollback functions”.

According to our conventional thinking, the interface can jump and go back, for example, interface A jumps to interface B, press the back key should go back to interface A, which is A standard stack data structure.

However, the start of the interface and the listening of the back key seem to be incompatible with the window, so Android encapsulates the Activity based on a single design principle, giving the window start and back functions, and AMS uniformly schedules the Activity stack and life cycle. Also through Demeter’s rule to screen window management inside and expose onCreate, onStart, onResume… Such a template approach lets developers focus only on View layout and lifecycle, regardless of window and stack structures

All Windows are derived from WMS, and the content of the window is filled by the View. The Activity only manages the window internally and coordinates the relationship between the window and View through WMS, and finally gives functions such as stack and life cycle.

tips

The task stack and return stack are not managed by the Activity. The Activity only provides the necessary conditions for the task stack and return stack. The access management is undertaken by AMS

How does an Activity manage Windows/Views? Look at section 2

2. Implementation process

The purpose of reading source code is to clarify the design process, do not cause and effect inversion into the details of the code, so we should know how to pick the key point, pay attention to point to the end. In order to provide a better reading experience, this article will delete most of the useless information in the source code, only retain the essence.

2.1 Origin of Activity

Where does Activity come from? If you want to trace the origin, I’m afraid you have to start with the creation of the first fertilized egg

Where did Zygote come from

The Android system creates the first process, init, from the Native layer at startup. The init process then parses a local file called init.rc to create Zygote

As the name suggests, Zygote’s job is the incubation process. All application processes are incubated by the Zygote process when the first SystemServer process is incubated and takes a back seat, waiting for the call to create the process through the Socket

Responsibilities of the SystemServer process

The SystemServer process is automatically created by Zygote and resides in memory for a long time. It registers various services such as:

  • ActivityManagerService(AMS):Create application process (notify zygote process via socket IPC)Management four components
  • WindowManagerService(WMS): used to open and manage the screenwindowTo keep the view displayed in an orderly manner
  • InputManagerService(IMS): Used for processing and distributing variousThe event
  • And so on…

Why put these system services in a separate process?

For example, AMS, WMS and IMS are all used to deal with some system-level tasks. For example, there is a concept of task stack/return stack in Activity. If the Activity is used to jump between applications, the relationship between task stack/return stack needs to be coordinated, and different applications belong to different processes. There is a need for a place to override all application processes, and a single process is the best choice. The same is true for other services such as WMS and IMS

Creating an application process

As mentioned earlier, AMS can notify Zygote of incubating application processes, but when? As you might have guessed, an app can be launched by clicking the app icon on the desktop, so that’s when AMS tells Zygote to create the app. But what is a desktop and where does it come from? The desktop is also an Activity, which is created automatically by AMS

Back to the subject, what is the process between clicking on the application icon and launching the Activity? Here’s a quick list:

  • When an App icon is clicked, the Binder IPC notifies AMS to create an application process if the corresponding application process has not already been created

  • When the application starts, it executes the familiar main method in the ActivityThread class, which corresponds to the Android main thread

  • The main method of ActivityThread first calls looper.loop (), which loops through messages distributed by the main thread Hanlder.

  • The next main method sends a BIND_APPLICATION message, which Looper receives via Binder IPC notifies AMS to create the corresponding Application for the App process

  • After Application is created, AMS will be notified by Binder IPC to create an Activity. After AMS is verified, AMS will return to the App process.

  • Back in the App process, ActivityThread#performLaunchActivity() is indirectly called to actually start the creation Activity, and attach() and onCreate() are executed.

tips

Application and Activity are not created directly through AMS, AMS is only responsible for management and verification, and the actual creation of specific objects also get the App process

Android View system is a very large concept, almost throughout the Java Framework, due to the author’s ability and space, the Java Framework is not explained clearly. So I’ll describe the origin of system processes, application processes, and activities to give you a clearer understanding of the Android view system.

2.2 PhoneWindow is not equivalent to “Window”

I didn’t describe Windows in the first section because I was afraid that people would confuse the two, because Window/PhoneWindow and real Window are two different concepts, and the author has been confused about this problem for a long time when reading the source code. Thank you very much to an immortal ape for giving me the answer in an article on the Windows mechanism of Android

Window is an abstract class in the Android SDK. It has a unique implementation class called PhoneWindow. Inside a PhoneWindow, there is a DecorView(root View) that does some standardized processing of the DecorView. Titles, backgrounds, navigation bars, events, etc., obviously do not fit the concept of Windows we mentioned earlier

When was the PhoneWindow created?

I mentioned that you can create an Activity with ActivityThread#performLaunchActivity(). Here’s the code:

#ActivityThread

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ...
        Activity activity = null;
        / / comment 1activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); .if(activity ! =null) {.../ / comment 2.activity.attach(...) ; ./ / comment 3.
            if (r.isPersistable()) {
                mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
            } else{ mInstrumentation.callActivityOnCreate(activity, r.state); }}...return activity;
}

Copy the code

First create an Activity object from comment 1, and then attach(..) it from comment 2. Method, and finally execute the Activity’s onCreate() method via callActivityOnCreate()

First let’s see what Attach did:

#Activity

final void attach(...){
      ...
      mWindow = new PhoneWindow(this, window, activityConfigCallback); . mWindow.setWindowManager(...) ; mWindowManager = mWindow.getWindowManager(); . }Copy the code

The Activity creates a PhoneWindow object in the Attach () method and copies it to the member variable mWindow, then executes the Setter and getter for WindowManager. Let’s focus on setter methods:

#Window

publicvoid setWindowManager(...) {...if (wm == null) {
            / / comment 1
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        / / comment 2
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }
Copy the code

Note 1 gets an object of type WindowManager from the system service to manage Windows.

Note 2 creates a WindowManagerImpl object through WindowManager, which is actually an interface that inherits from the ViewManager interface and WindowManagerImpl is one of its implementation classes

Creating another WindowManager through WindowManager seems redundant, so why is Android doing this?

First, WindowManager has two responsibilities: managing Windows and creating Windows Manager. The WindowManager obtained by the system service has the ability to create Windows, but is not associated with any Windows at this time. A WindowManager created through createLocalWindowManager is one-to-one bound to the corresponding Window. So the former is used to create WindowManager, while the latter is used to bind Windows one-to-one. Both responsibilities are clear, but the author is puzzled why not extract the creation process to another class based on a single design principle? If you know the students can leave a message in the comments section, thank you in advance ~

How does Windows ManagerImpl manage Windows

The PhoneWindow has been created, but not yet associated with the Activity/View. Take a look at the PhoneWindow source code and you’ll find that it only sets the title, background, and event transitions inside the PhoneWindow, so don’t confuse the two

2.3 When a DecorView is created

According to 2.2, onCreate() will be executed after attach() of the Activity is finished. Usually, we need to execute stContentView() in onCreate() to display the XML Layout. StContentView () sets up our ContentView as follows:

#Activity

public void setContentView(@LayoutResint layoutResID) { getWindow().setContentView(layoutResID); . }public Window getWindow() {
      return mWindow;
}
Copy the code

Get the PhoneWindow created in attach() by getWindow(), and then pass layoutResID(XML Layout) into it.

#PhoneWindow

ViewGroup mContentParent;

public void setContentView(int layoutResID) {
        / / comment 1
        if (mContentParent == null) {
            installDecor();
        } else if(! hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); }if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
           ...
        } else {
            / / comment 2mLayoutInflater.inflate(layoutResID, mContentParent); }}Copy the code

Comment 1 determines if the mContentParent is empty. If it is, instantiate it with installDecor(), otherwise remove all child Views.

Comment 2 loads the XML corresponding to layoutResID into mContentParent. The only question so far is how mContentParent is created, follow installDecor() :

#PhoneWindow

private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor(-1); . }else {
            mDecor.setWindow(this);
        }
        if (mContentParent == null) { mContentParent = generateLayout(mDecor); . }}Copy the code

First create a DecorView type object and assign it to reference mDecor. So what is a DecorView?

DecorView inherits FrameLayout with a vertical LinearLayout for the status bar background, TitleBar, ContentView, navigation bar background, The ContentView is used to hold the Layout passed in by Activity#setContentView. DecorView is designed because the status bar background, title bar, navigation bar background, and so on need to be managed by the system. Finally, Demeter’s rule hides its administrative operations internally, exposing only the ContentView to be populated by the developer.

Note that the DecorView just fills the status bar, navigation bar with background, battery, signal... It is uniformly drawn by the system

Back to the mDecor process, generateDecor(-1) code:

#PhoneWindow

protected DecorView generateDecor(int featureId) {
    ...
    return new DecorView(context, featureId, this, getAttributes());
}
Copy the code

Just new out a DecorView. To return to our original question, where does mContentParent come from? DecorView creates mContentParent by generateLayout(mDecor). GenerateLayout (mDecor) code is too long to post, interior decor will get mContentParent through mDecor and set theme, background, etc.

At this point, the DecorView is created and associated with the XML Layout, but the root View is not associated with the window, so it is not visible.

Why do I do setContentView on onCreate?

You can create a DecorView with setContentView, whereas an Activity usually has only one DecorView(skim Dialog, etc.), Placing setContentView in start and Resume can create multiple decorViews, which can be wasteful. So onCreate is the best time to create a DecorView

2.4 How does ViewRootImpl coordinate the relationship between View and Window?

After an Activity is started, it calls the corresponding lifecycle methods through ActivityThread at different times. OnResume is a special time when it is called through ActivityThread#handleResumeActivity, with the following code:

#PhoneWindow

public void handleResumeActivity(...) {
        / / comment 1
        finalActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason); .finalActivity a = r.activity; ./ / comment 2r.window = r.activity.getWindow(); View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); ./ / comment 3wm.addView(decor, l); . }Copy the code
  • This is called indirectly at comment 1ActivitytheonResumemethods
  • Adopted at comment 2ActivityTo obtainPhoneWindow, DecorView, WindowManagerIf you forget, you can go back and read them.
  • Called at comment 3WindowManagertheaddViewThe method, as the name implies, is toDecorViewAdded to theWindowThis step is crucial

As mentioned in the 2.2 summary of WindowManager concepts, it is an interface that has an implementation class WindowManagerImp, followed by its addView() method

#WindowManagerImp

private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

publicvoid addView(...) {... mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow, mContext.getUserId()); . }Copy the code

AddView () is an internal call to the mGlobal addView() method. Almost all WindowManager methods are implemented by delegating to mGlobal. Let me make the following three points:

  • WindowManagerThe functionality provided is universal and does not correspond to oneView/WindowSeparate bindings, one should be designed to save memoryThe singleton.
  • WindowManagerImpHaving multiple responsibilities such asToken management and Windows Manager functionsEtc., so passSingle design principlewillWindowManager functionTo split into another classWindowManagerGlobalAnd defines it as a singleton.
  • In order not to violateDemeter's ruleAnd through the combination modeWindowManagerGlobalShielded from the inside.

To get back to business, consider mGlobal’s addView() method:

#WindowManagerGlobal
/** * to store all decorViews */
private final ArrayList<View> mViews = new ArrayList<View>();
/** * to store all ViewRootImpl */
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
/** * to store all LayoutParams */
private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();

publicvoid addView(...) {... ViewRootImpl root; synchronized (mLock) { root = new ViewRootImpl(view.getContext(), display); mViews.add(view); mRoots.add(root); mParams.add(wparams); . root.setView(view, wparams, panelParentView, userId); . }}Copy the code

Create a View otimPL object root, then add view, root, and wparams to the corresponding collection, which is managed by the Windows Global singleton, and finally execute root’s setView(). Based on my years of reading source code, the answer should be in root.setview ()

ViewRootImpl
public void setView(...) {
        synchronized (this) {
            if (mView == null) {... mView = view; ./ / comment 1
                requestLayout();
                / / comment 2res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame, mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mDisplayCutout, inputChannel, mTempInsets, mTempControls); ./ / comment 3
                view.assignParent(this);
            }
        }
    }

void assignParent(ViewParent parent) {
        if (mParent == null) {
            mParent = parent;
         } else if (parent == null) {
            mParent = null; }... }Copy the code

The viewrotimpl #setView() method is a long one, and I’ve done a little recap to list a few key steps

  • Note 1.requestLayout()A chain of calls will eventually open upmView(DecorView)theMeasure, layout, draw. This process is very complex, for reasons of space, this article will not mention, interested can refer toChoreographerThe relevant knowledge
  • Note 2,mWindowSessionIs aIWindowSessionThe type ofAIDLThe file, it will passBinder IPCnoticeWMSOpen up a window on the screen aboutWMSThe implementation process is also very large, so let’s leave it at that. This step completes ourViewAnd you can display it on the screen
  • Note 3, the last step is executedView#assignParentInternal will,mParentSet toViewRootImpl. So, althoughViewRootImplIs not aViewBut it is allViewThe top of theParent

I mentioned at the beginning of the summary that many people equate Window/PhoneWindow in the API to Window, but in fact it is ViewRootImpl that opens the Window and manages the View drawing, which is the most critical part of the View system.

confusion

I often hear people say that the onStart phase is in visible mode, which I find confusing. The onResume window is created and the DecorView is opened, so there is no window in onStart.

Note:

When learning Android for the first time, I often make the mistake of obtaining the View width and height when onCreate is used. The reason is that the View is drawn after onResume, so the View width and height state cannot be obtained before then. At this point you can through the post {} or addOnGlobalLayoutListener to obtain high wide

The implementation of the Java Framework level view system is very complex, so I’ve listed the key classes and their corresponding responsibilities for your understanding

  • WindowIs an abstract class, through controlDecorViewProvides some standard UI solutions, such asBackground, title, virtual buttons, etc
  • PhoneWindowisWindowThe only implementation class of theWindowThe function is providedThe eventtransit
  • WindowManagerIs an interface that inherits fromViewManagerInterface, providedViewThe basic operation method of
  • WindowManagerImpTo achieve theWindowManagerInterface, internal throughcombinationWay to holdWindowManagerGlobal, used to operateView
  • WindowManagerGlobalIs a global singleton that can pass internallyViewRootImplwillViewAdded to thewindowIn the
  • ViewRootImplIs that allViewtheParentTo manageViewAnd the drawing ofwindowThe opening
  • IWindowSessionisIWindowSessionThe type ofAIDLInterface, can passBinder IPCnoticeWMSOpen the window

At this point, the design and implementation of Java Framework level view system are sorted out

From what has been discussed above

  • All views are created byCanvasAnd to
  • ViewIs present to provide viewsThe templateTo improve development efficiency
  • windowYou can makeViewOrderly display
  • ActivityFor eachwindowIncrease the life cycle, letwindowSwitching is more elegant
  • PhoneWindowJust provide some standard UI solutions, andwindowinequitable
  • throughWindowManagerwillViewAdded to thewindow
  • ViewRootImplIs the role of opening the window, and managingViewRendering is the most critical part of the view system
  • The intricacies of the view system are largely hiddenActivityInternally, developers can develop only based on the template approach

reference

Android’s Window mechanism for full parsing

Who woke up Zygote in the Android world?