preface

The previous article analyzed the Activity startup process.

How does the layout file set by setContentView get typesetted and displayed to the device after startup?

The same is a platitude, online access to a lot of relevant information, here to make a review summary record.

The plan is divided into three articles to analyze and sort out the main process of Android drawing in general.

The goal is to establish an overall understanding of the drawing process and pave the way for further understanding of internal details.

If there is any mistake, please point it out and make progress together

Source code analysis

Start with a brief diagram of creating a Window and establishing a connection with SurfaceFlinger.

Based on Android 11 (Andoird R), details may vary between versions.

On all processes that require display functionality, contact SurfaceFlinger before drawing can begin.

This article will start from the Activity layout setting, how the View is associated with Window, SurfaceFlinger, a rough, process exploration.

1. Build a view tree

In real development, we usually load and import the layout file through setContentView() in the activity.oncreate () method, and we use this as a starting point for analysis.

Take Android 11 (Andoird R) as an example. Due to the limited space and the complicated actual source code process, only the important core code is reserved here.

// android.app.Activity
public class Activity extends ContextThemeWrapper.{
   	private Window mWindow;
    
    public void setContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID); . }public Window getWindow(a) {
        returnmWindow; }}Copy the code

Window is an abstract class, and we need to see where the Activity gets the Window object.

Remember the activity.attach () method that was called when the Activity was created during the Activity launch process?

The Window object is being created here for its subclasses com. Android. Internal. Policy. PhoneWindow.

// android.app.Activity
public class Activity extends ContextThemeWrapper.{
    private WindowManager mWindowManager;
    
    final void attach(Context context, ActivityThread aThread, ... , Window window, ActivityConfigCallback activityConfigCallback, ...){ attachBaseContext(context); . mWindow =new PhoneWindow(this, window, activityConfigCallback); .// Set up windowManager(note this windowManager)mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),...) ; .// Assign an internal variable to the activitymWindowManager = mWindow.getWindowManager(); . }}//com.android.internal.policy.PhoneWindow
public class PhoneWindow extends Window.{
    ViewGroup mContentParent;
    
    @Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            //mContentParent is empty and creates a top-level DecorView
            installDecor();
        } else if(! hasFeature(FEATURE_CONTENT_TRANSITIONS)) {// The ContentParent container already exists, empty all child ViewsmContentParent.removeAllViews(); }...// Add the control corresponding to the Content layout file parsing layer to the ContentParent containermLayoutInflater.inflate(layoutResID, mContentParent); . }private void installDecor(a) {
        if (mDecor == null) {
            // Create a top-level View object
            mDecor = generateDecor(-1); . }else {
            mDecor.setWindow(this);
        }
        
        if (mContentParent == null) {
            // Add a root container to the top ViewmContentParent = generateLayout(mDecor); . }}/ / create a DecorView
    protected DecorView generateDecor(int featureId) {...return newDecorView(context,...) ; }// View ID of the content part of the root container in the parent class Window
    public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
    
    protected ViewGroup generateLayout(DecorView decor) {
        // Load the corresponding top-level View layout according to the Theme style
        intlayoutResource; .// The simplest root layout, for example
        layoutResource = R.layout.screen_simple;    
        // Add a child View to the top View and set the corresponding layoutmDecor.onResourcesLoaded(mLayoutInflater, layoutResource); ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); .return contentParent
    }
    
    // The parent Window method gets the Content part of the container ViewGroup through the layout set in the DecorView
    @Nullable
    public <T extends View> T findViewById(@IdRes int id) {
        returngetDecorView().findViewById(id); }}Copy the code

A DecorView is created inside the PhoneWindow as a top-level View container that wraps all other views and inherits itself from FrameLayout.

public class DecorView extends FrameLayout.{

	void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {...final View root = inflater.inflate(layoutResource, null); .// Add the Content layout to the ViewGroup
        addView(root, 0.newViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); . }}Copy the code
  • So fromDecorViewThrough thefindViewByIdTo get tocontentParentWhat is it?

Take the simplest of these r.layout.screen_simple layouts

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<ViewStub android:id="@+id/action_mode_bar_stub"
        android:inflatedId="@+id/action_mode_bar"
        android:layout="@layout/action_mode_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="? attr/actionBarTheme" />
<FrameLayout
   android:id="@android:id/content"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:foregroundInsidePadding="false"
   android:foregroundGravity="fill_horizontal|top"
   android:foreground="? android:attr/windowContentOverlay" />
</LinearLayout>
Copy the code

Get in PhoneWindow com. Android. Internal. R.i, dc ontent is one of the FrameLayout.

So our normal setContent is, by default, we already have these basic view hierarchies.

  • Summary:

The process of setContentView() is simply the process of loading the View that has been parsed with XML by the layout inflate and nesting it into the View tree with the DecorView as the root node.

About LayoutInflater. Inflat see [Android | belt you explore LayoutInflater layout principle] (

2. Window Establishes the connection

After analyzing the view tree construction, let’s go back to ActivityThread to see when the view tree is associated with the Window.

Remember the handleResumeActivity method called during the Activity launch process?

// ============== android.app.ActivityThread ================
public final class ActivityThread extends ClientTransactionHandler {
    
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, ...) {...// The activity.onresume () method is executed internally
        finalActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason); .if (r.window == null && !a.mFinished && willBeVisible) {
            // Is a newly started Activity
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            // Make the DecorView view tree invisibledecor.setVisibility(View.INVISIBLE); ViewManager wm = a.getWindowManager(); .if (a.mVisibleFromClient) {
                if(! a.mWindowAdded) { a.mWindowAdded =true;
                    // Add the view tree to WindowManager
                    wm.addView(decor, l);
                }else{... }}}...if (r.activity.mVisibleFromClient) {
            // Set the DecorView view to visibler.activity.makeVisible(); }... }}Copy the code

When the Activity is first started, the view is added to The WindowManager after onResume() is executed, and the activity.makevisible method is called to switch to the visible state.

This is why you can’t get the size of the View from onCreate() to onResume. After all, you haven’t done any UI drawing yet, so you can’t have dimensions.

// =========== android.app.Activity ==================
public class Activity extends ContextThemeWrapper.{
    View mDecor = null;
    
    void makeVisible(a) {
        if(! mWindowAdded) { ViewManager wm = getWindowManager(); wm.addView(mDecor, getWindow().getAttributes()); mWindowAdded =true;
        }
        mDecor.setVisibility(View.VISIBLE);
    }
    
    public WindowManager getWindowManager(a) {
        returnmWindowManager; }}Copy the code

But WindowManager is an interface that inherits from ViewManager, so where are instances of its implementation classes?

// ============= android.view.WindowManager ===============
public interface WindowManager extends ViewManager {... }public interface ViewManager{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}
Copy the code

Mentioned Activity. GetWindowManager (), finally also is through the Window. The getWindowManager () to obtain.

In Windows, the value is assigned by calling window.setwindowManager in the activity.attch () method mentioned earlier.

// ============= android.view.Window ==============
public abstract class Window {
    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated;
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this); }}Copy the code

Can see the actual assignment to mWindowManager is its implementation subclass android. The WindowManagerImpl.

// ============ android.view.WindowManagerImpl ===================
public final class WindowManagerImpl implements WindowManager {
    
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    
    private WindowManagerImpl(Context context, Window parentWindow) {
        mContext = context;
        mParentWindow = parentWindow;
    }
    
    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }
    
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        // The agent performs the add operation on the singleton classmGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow, mContext.getUserId()); }}Copy the code

In Windows ManagerImpl, Windows ManagerGlobal creates a new ViewRootImpl object by performing the addView operation.

// =========== android.view.WindowManagerGlobal =================

public final class WindowManagerGlobal {
    private static WindowManagerGlobal sDefaultWindowManager;
    
    private WindowManagerGlobal(a) {}// Global singleton class
    public static WindowManagerGlobal getInstance(a) {
        synchronized (WindowManagerGlobal.class) {
            if (sDefaultWindowManager == null) {
                sDefaultWindowManager = new WindowManagerGlobal();
            }
            returnsDefaultWindowManager; }}public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow, int userId) {... ViewRootImpl root; .Create a new ViewRootImpl
        root = newViewRootImpl(view.getContext(), display); .// Add a DecorView to ViewRootImplroot.setView(view, wparams, panelParentView, userId); }}Copy the code

WindowManagerGlobal is a global singleton in an application process that manages all Windows in the process.

Set the DecorView view tree to ViewRootImpl via viewrootimpl.setView.

// ============ android.view.ViewRootImpl =================

public final class ViewRootImpl implements ViewParent.{
    // WindowManagerService Binder agent class
    final IWindowSession mWindowSession;
    final W mWindow;
    
    View mView;
    
    public ViewRootImpl(Context context, Display display) {
        // The default value for constructor polymorphism, not the actual code
        mWindowSession = WindowManagerGlobal.getWindowSession();
    }
    
	public void setView(View view, WindowManager.LayoutParams attrs, 
                        View panelParentView,int userId) {
        synchronized (this) {
            if (mView == null) {
                DecorView / / assignmentmView = view; }...// Request to draw a view, as described in the next articlerequestLayout(); .int res; /* = WindowManagerImpl.ADD_OKAY; * /.// A cross-process Binder proxy call to establish connections through the WindowManagerService of the system processres = mWindowSession.addToDisplayAsUser(mWindow,...) ; . }}}Copy the code

2.1 with SurfaceSession WindowState

Binder proxy objects in view Windows GLOBAL are retrieved through the Windows ManagerGlobal static methods.

The agent implementation class of IWindowManager is WindowManagerService in system_server process.

And IWindowSession is founded by WindowManagerService proxy class through openSession method com. Android. Server. Wm. The Session.

// ============ android.view.WindowManagerGlobal ===================

public final class WindowManagerGlobal {
    // Static method that gets a singleton object of WindowManagerService's Binder proxy class
    public static IWindowManager getWindowManagerService(a) {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window"));
                try {
                    if(sWindowManagerService ! =null) { ValueAnimator.setDurationScale( sWindowManagerService.getCurrentAnimatorScale()); sUseBLASTAdapter = sWindowManagerService.useBLAST(); }}catch (RemoteException e) {
                    throwe.rethrowFromSystemServer(); }}returnsWindowManagerService; }}// Static method to get the singleton of the Session proxy class
    public static IWindowSession getWindowSession(a) {
        synchronized (WindowManagerGlobal.class) {
            if(sWindowSession == null) {/ / initialization
                InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();    
                // Gets the WindowManagerService proxy class
                IWindowManager windowManager = getWindowManagerService();
                // Start a Session using WindowManagerService and obtain the proxy object of the Session
                sWindowSession = windowManager.openSession(
                    new IWindowSessionCallback.Stub() {
                        @Override
                        public void onAnimatorScaleChanged(float scale) { ValueAnimator.setDurationScale(scale); }}); . }return sWindowSession    
        }
    }
}

// ============ com.android.server.wm.WindowManagerService ==============

public class WindowManagerService extends IWindowManager.Stub.{
    
    @Override
    public IWindowSession openSession(IWindowSessionCallback callback) {
        return new Session(this, callback); }}// ============== com.android.server.wm ================
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
    final WindowManagerService mService;
    
    @Override
    public int addToDisplayAsUser(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, int userId, ...) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,...);
    }
        
}
Copy the code

Session is more of a passing intermediate class, and the actual work is back to WindowManagerService.

// ============== com.android.server.wm.WindowManagerService =================

/** Mapping from an IWindow IBinder to the server's Window object. */
final HashMap<IBinder, WindowState> mWindowMap = new HashMap<>();

public int addWindow(Session session, IWindow client, intseq, LayoutParams attrs, ... .int displayId,...) {...// Create the Window state class, WindowManagerService also manages WindowState
    final WindowState win = new WindowState(this, session, client , ...) ; . win.attach(); mWindowMap.put(client.asBinder(), win); win.initAppOpsState(); . }Copy the code

In addWindow, a new WindowState object, the abstract Window container, is created and its attach method is executed.

The Attach operation is performed by the internally held Session, and the SurfaceSession object is created internally.

// ============= com.android.server.wm.WindowState ==================

class WindowState extends WindowContainer<WindowState>...{
    final Session mSession;
    final WindowStateAnimator mWinAnimator;
    
    WindowState(WindowManagerService service, Session s, IWindow c, ...) {
     	mSession = s;
        mWinAnimator = new WindowStateAnimator(this); . }void attach(a) { mSession.windowAddedLocked(mAttrs.packageName); }}// ================ com.android.server.wm.Session =================

class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
    SurfaceSession mSurfaceSession;
    final WindowManagerService mService;
    
    void windowAddedLocked(String packageName) {...if (mSurfaceSession == null) {...// Key code
            mSurfaceSession = newSurfaceSession(); .// Add the current Session object to the associated Session list
            mService.mSessions.add(this); . } mNumWindow++; }}Copy the code

SurfaceSession is an empty shell that holds only a native object reference address created by nativeCreate

// ============ android.view.SurfaceSession ===============
public final class SurfaceSession {
    // Note: This field is accessed by native code.
    private long mNativeClient; // SurfaceComposerClient*

    private static native long nativeCreate(a);
    
    /** Create a new connection with the surface flinger. */
    public SurfaceSession(a) { mNativeClient = nativeCreate(); }}Copy the code

2.2 SurfaceComposerClient

SurfaceSession nativeCreate is a native method, we try to continue to explore downward

For more information about viewing native methods, see Android JNI Principle analysis

The following native methods are viewed through Android Search Code, which requires climbing the wall.

Code based on Android11-RC3, limited space, also only retain the key core code.

Native methods in the C file name for the fully qualified Java class name usually android. The SurfaceSession, will be. Change to _, android_view_SurfaceSession. CPP.

// ============== frameworks/base/core/jni/android_view_SurfaceSession.cpp ==============
static jlong nativeCreate(JNIEnv* env, jclass clazz) {
    SurfaceComposerClient* client = new SurfaceComposerClient(a);// Add a strong reference count
    client->incStrong((void*)nativeCreate);
    return reinterpret_cast<jlong>(client);
}
Copy the code

The SurfaceComposerClient is actually created here and is actually commented out in the Java source code.

// ============ frameworks/native/libs/gui/SurfaceComposerClient.cpp ===============
SurfaceComposerClient::SurfaceComposerClient()
    : mStatus(NO_INIT)
{       
}
Copy the code

The SurfaceComposerClient constructor is an empty implementation. However, after the instance is created in nativeCreate(), the strong reference count is also increased by incStrong.

For incStrong to increase the number of citations, see:

Android Framwork analysis smart pointer LightRefBase, SP, WP,RefBase

The design idea of Intelligent pointer of Android system (lightweight pointer, strong pointer, weak pointer)

// ============ frameworks/native/libs/gui/include/gui/SurfaceComposerClient.h ===============
class SurfaceComposerClient : public RefBase
{
    ...
}
Copy the code

The SurfaceComposerClient inherits from RefBase and executes the onFirstRef() method after the first strong reference is added.

// ========== frameworks/native/libs/gui/SurfaceComposerClient.cpp ================
void SurfaceComposerClient::onFirstRef(a) {
    // Obtain SurfaceFlinger's Binder proxy object
    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
    if(sf ! =nullptr && mStatus == NO_INIT) {
        sp<ISurfaceComposerClient> conn;
        // Connect to SurfaceFlinger and create a Client
        conn = sf->createConnection(a);if(conn ! =nullptr) { mClient = conn; mStatus = NO_ERROR; }}}/* * This class defines the Binder IPC interface for accessing various * SurfaceFlinger features. */
class ISurfaceComposer: public IInterface {
    /* * Create a connection with SurfaceFlinger. */
    virtual sp<ISurfaceComposerClient> createConnection(a) = 0;
}
Copy the code

The ISurfaceComposer is actually SurfaceFlinger’s Binder proxy class. The Binder proxy calls the SurfaceFlinger method in a separate process.

We see SurfaceFlinger directly. The createConnection.

// ======== frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp =============

sp<ISurfaceComposerClient> SurfaceFlinger::createConnection(a) {
    const sp<Client> client = new Client(this);
    return client->initCheck() == NO_ERROR ? client : nullptr;
}

// frameworks/native/libs/gui/include/gui/ISurfaceComposerClient.h
class ISurfaceComposerClient : public IInterface {
    ...
}

// ========== frameworks/native/services/surfaceflinger/Client.cpp ================
Client::Client(const sp<SurfaceFlinger>& flinger)
    : mFlinger(flinger)
{
}

status_t Client::createSurface(...). {
    // We rely on createLayer to check permissions.
    return mFlinger->createLayer(...). ; }Copy the code

The Client objects created internally are the Binder proxy implementation classes for isurFace Client, and the internal operations are handed over to SurfaceFlinger calls.

A new Client object is created each time a connection is established.

This allows SurfaceSession to connect to SurfaceFlinger via the SurfaceComposerClient.

Quote from official documentation:

SurfaceFlinger takes buffers of data from multiple sources, composes them, and sends them to display devices.

When the application comes to the foreground, the WindowManager service requests a drawing Surface from SurfaceFlinger. SurfaceFlinger creates a layer whose main component is BufferQueue, and SurfaceFlinger is its consumer. Binder objects on the production side are passed to the application through WindowManager, and the application can then start sending frames directly to SurfaceFlinger.

Therefore, SurfaceFlinger is used as a key node associated with the display device, and finally synthesized Layer rendering and displayed to the device.

For more on SurfaceFlinger see Synonyms at:

Android Systrace Basics (5) – SurfaceFlinger interpretation

Android-SurfaceFlinger starts with drawing principles

An article to understand the relationship between The Android graphics system Surface and SurfaceFlinger

Android Graphics Architecture Ii — SurfaceFlinger launches and connects

The understanding of Binder mechanism for cross-process communication is still a little vague. It is briefly regarded as the application of simple proxy mode, which will be further studied later.

conclusion

Finally, summarize the general process of this part:

  1. DecorView creates a new ViewRootImpl via Windows ManagerGlobal via the addView() method and adds the view tree built in the previous section to the ViewRootImpl.

  2. ViewRootImpl after a series of calls, finally passed across processes Binder agent call WindowManagerService. AddWindow (), create a new WindowState.

  3. Session creates a new native layer SurfaceComposerClient, which holds Binder proxy objects of the newly created Client and connects to SurfaceFlinger.

    SurfaceFlinger is in a separate process, and the App can draw views only after a connection is established

The next article continues the view drawing process.

The resources

DecorView introduction

In this article, we will understand the relationship between Activity and Window and View

Android Graphics Architecture Ii — SurfaceFlinger launches and connects

Android UI display principle of Surface creation

Graphic | a figure out the Android system services