We’ll start by analyzing the View’s drawing system by looking at how ActivityThread creates Activity instances and calls their lifecycle callbacks one by one.

The start step of the Activity

1. Click the desktop App icon, and the Binder IPC sends the startActivity request to the System_server process;

2. After receiving the request, the system_server process sends a request to zygote to create a process.

3. Zygote forks a new child process, that is, App process.

4. The App uses Binder IPC to request attachApplication to sytem_server.

5. After receiving the request, the System_server process performs a series of preparatory work and sends the scheduleLaunchActivity request to App process through Binder IPC;

6. The binder threads of the App process send LAUNCH_ACTIVITY messages to the main thread through the handler upon receiving the request.

ActivityThread.scheduleLaunchActivity()

// we use token to identify this activity without having to send the // activity itself back to the activity manager. (matters more with ipc) @Override public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) { updateProcessState(procState, false); ActivityClientRecord r = new ActivityClientRecord(); . r.token = token; r.ident = ident; r.intent = intent; . r.overrideConfig = overrideConfig; updatePendingConfiguration(curConfig); sendMessage(H.LAUNCH_ACTIVITY, r); }Copy the code

SendMessage, finally called mh. sendMessage(MSG); MH is an instance of class H, which is a subclass of Handler. After sending the message, look at the handleMessage() method of class H

                case LAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;
Copy the code

Continue to execute ActivityThread handleLaunchActivity

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) { ... . Activity a = performLaunchActivity(r, customIntent); . . if (a ! = null) { .... handleResumeActivity(r.token, false, r.isForward, ! r.activity.mFinished && ! r.startsNotResumed, r.lastProcessedSeq, reason); . if (! r.activity.mFinished && r.startsNotResumed) { performPauseActivityIfNeeded(r, reason); }... }else{ // If there was an error, for any reason, tell the activity manager to stop us. try { ActivityManager.getService() .finishActivity(r.token, Activity.RESULT_CANCELED, null, Activity.DONT_FINISH_TASK_WITH_ACTIVITY); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); }}... . }Copy the code

7. After receiving the Message, the main thread creates the target Activity through the launch mechanism and calls the activity.oncreate () and other methods.

ActivityThread. PerformLaunchActivity collected here to launch the Activity of relevant information, mainly is the package and component information, then this is going to start the Activity class loading.

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; . 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); }... . if (activity ! = null) { .... 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 (r.isPersistable()) { mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnCreate(activity, r.state); }... . r.activity = activity; r.stopped = true; if (! r.activity.mFinished) { activity.performStart(); r.stopped = false; }... . }... . } public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException { return (Activity)cl.loadClass(className).newInstance(); }Copy the code

The Activity instance is associated with the View’s drawing system from the attch() function:

The attach() method of the Activity

The Attach () method of the Activity is an initialization procedure, which is called in the ActivityThread’s performLaunchActivity() method and does two things:

  1. Initialize a Window, where the Window constructor does a few things, for example, initialization of layoutInflaters that we’re interested in.

  2. Initialize WindowManager and set to Window.

    Activity.java private Window mWindow;

    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, ActivityConfigCallback activityConfigCallback) { ... . mWindow = new PhoneWindow(this, window, activityConfigCallback); mWindow.setWindowControllerCallback(this); mWindow.setCallback(this); mWindow.setOnWindowDismissedCallback(this); mWindow.getLayoutInflater().setPrivateFactory(this); . . . mWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) ! = 0); if (mParent ! = null) { mWindow.setContainer(mParent.getWindow()); } mWindowManager = mWindow.getWindowManager(); .Copy the code

    }

The onCreate() method of the Activity

The setContentView in the Activity onCreate() calls getWindow().setContentView ()

The actual call is PhoneWindow. The setContentView () :

// 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. ViewGroup mContentParent; @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 crystalized. Do not check the feature // before this happens. if (mContentParent == null) { installDecor(); } else if (! hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); } if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {final Scene newScene = scene.getSceneForLayout (mContentParent, FEATURE_CONTENT_TRANSITIONS) layoutResID, getContext()); transitionTo(newScene); } else { mLayoutInflater.inflate(layoutResID, mContentParent); } mContentParent.requestApplyInsets(); final Callback cb = getCallback(); if (cb ! = null && ! isDestroyed()) { cb.onContentChanged(); } mContentParentExplicitlySet = true; }Copy the code

If mContentParent is empty for the first time, run installDecor, where mDecor is created in generateDecor if mContentParent is empty:

PhoneWindow.java private void installDecor() { mForceDecorInstall = false; if (mDecor == null) { mDecor = generateDecor(-1); mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); if (! mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures ! = 0) { mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); } } else { mDecor.setWindow(this); }... If (mContentParent == null) {// Returns the content area view of the current Activity, that is, the display area of our layout file. MContentParent = generateLayout(mDecor); // Initialize a bunch of attributes... . . }}Copy the code

Once the DecorView is successfully created, the next step is to create the mContentParent via generateLayout. (mContentParent is a Viewgroup object) :

protected ViewGroup generateLayout(DecorView decor) {  
    // Apply data from current theme.  
   //获取window属性
    TypedArray a = getWindowStyle();  
    if (false) {  
        System.out.println("From style:");  
        String s = "Attrs:";  
        for (int i = 0; i < com.android.internal.R.styleable.Window.length; i++) {  
            s = s + " " + Integer.toHexString(com.android.internal.R.styleable.Window[i]) + "="  
                    + a.getString(i);  
        }  
        System.out.println(s);  
    }  
    //判断窗口是否浮动
    mIsFloating = a.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating, false);  
    int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)  
            & (~getForcedWindowFlags());  
    if (mIsFloating) {  
        setLayout(WRAP_CONTENT, WRAP_CONTENT);  
        setFlags(0, flagsToUpdate);  
    } else {  
        setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);  
    }  

    //判断窗口是否有标题
    if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) {  
        requestFeature(FEATURE_NO_TITLE);  
    }  
    //判断窗口是否满屏
    if (a.getBoolean(com.android.internal.R.styleable.Window_windowFullscreen, false)) {  
        setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN&(~getForcedWindowFlags()));  
    }  
    //判断窗口是否
    if (a.getBoolean(com.android.internal.R.styleable.Window_windowShowWallpaper, false)) {  
        setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags()));  
    }  
    WindowManager.LayoutParams params = getAttributes();  
//设置输入法模式
    if (!hasSoftInputMode()) {  
        params.softInputMode = a.getInt(  
                com.android.internal.R.styleable.Window_windowSoftInputMode,  
                params.softInputMode);  
    }  
    if (a.getBoolean(com.android.internal.R.styleable.Window_backgroundDimEnabled,  
            mIsFloating)) {  
        /* All dialogs should have the window dimmed */  
        if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) {  
            params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;  
        }  
        params.dimAmount = a.getFloat(  
                android.R.styleable.Window_backgroundDimAmount, 0.5f);  
       //动画
    }  
    if (params.windowAnimations == 0) {  
        params.windowAnimations = a.getResourceId(  
                com.android.internal.R.styleable.Window_windowAnimationStyle, 0);  
    }  
    // The rest are only done if this window is not embedded; otherwise,  
    // the values are inherited from our container.  
    if (getContainer() == null) {  
        if (mBackgroundDrawable == null) {  
            if (mBackgroundResource == 0) {  
                mBackgroundResource = a.getResourceId(  
                        com.android.internal.R.styleable.Window_windowBackground, 0);  
            }  
            if (mFrameResource == 0) {  
                mFrameResource = a.getResourceId(com.android.internal.R.styleable.Window_windowFrame, 0);  
            }  
            if (false) {  
                System.out.println("Background: "  
                        + Integer.toHexString(mBackgroundResource) + " Frame: "  
                        + Integer.toHexString(mFrameResource));  
            }  
        }  
        mTextColor = a.getColor(com.android.internal.R.styleable.Window_textColor, 0xFF000000);  
    }  
    **以上都是设置一些window的属性,这里也说明设置window属性要在setContentView()之前设置的原因了。**

    // Inflate the window decor.  

    **//下面是根据各种情况设置layoutResource**

    int layoutResource;  
    int features = getLocalFeatures();  
    // System.out.println("Features: 0x" + Integer.toHexString(features));  
    if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {  
        if (mIsFloating) {  
            layoutResource = com.android.internal.R.layout.dialog_title_icons;  
        } else {  
            layoutResource = com.android.internal.R.layout.screen_title_icons;  
        }  
        // System.out.println("Title Icons!");  
    } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0) {  
        // Special case for a window with only a progress bar (and title).  
        // XXX Need to have a no-title version of embedded windows.  
        layoutResource = com.android.internal.R.layout.screen_progress;  
        // System.out.println("Progress!");  
    } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {  
        // Special case for a window with a custom title.  
        // If the window is floating, we need a dialog layout  
        if (mIsFloating) {  
            layoutResource = com.android.internal.R.layout.dialog_custom_title;  
        } else {  
            layoutResource = com.android.internal.R.layout.screen_custom_title;  
        }  
    } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {  
        // If no other features and not embedded, only need a title.  
        // If the window is floating, we need a dialog layout  
        if (mIsFloating) {  
            layoutResource = com.android.internal.R.layout.dialog_title;  
        } else {  
            layoutResource = com.android.internal.R.layout.screen_title;  
        }  
        // System.out.println("Title!");  
    } else {  
        // Embedded, so no decoration is needed.  
        layoutResource = com.android.internal.R.layout.screen_simple;  
        // System.out.println("Simple!");  
    }  
    mDecor.startChanging();  


    View in = mLayoutInflater.inflate(layoutResource, null);  
    //将layoutResource转化成View添加到decor中,
    decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));  
//然后又通过findViewById找到该view,

    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);  
    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);  
        }  
    }  
    // Remaining setup -- of background and title -- that only applies  
    // to top-level windows.  
    if (getContainer() == null) {  
        Drawable drawable = mBackgroundDrawable;  
        if (mBackgroundResource != 0) {  
            drawable = getContext().getResources().getDrawable(mBackgroundResource);  
        }  
        mDecor.setWindowBackground(drawable);  
        drawable = null;  
        if (mFrameResource != 0) {  
            drawable = getContext().getResources().getDrawable(mFrameResource);  
        }  
        mDecor.setWindowFrame(drawable);  
        // System.out.println("Text=" + Integer.toHexString(mTextColor) +  
        // " Sel=" + Integer.toHexString(mTextSelectedColor) +  
        // " Title=" + Integer.toHexString(mTitleColor));  
        if (mTitleColor == 0) {  
            mTitleColor = mTextColor;  
        }  
        if (mTitle != null) {  
            setTitle(mTitle);  
        }  
        setTitleColor(mTitleColor);  
    }  
    mDecor.finishChanging();  
    //然后返回
    return contentParent;  

} 
Copy the code

In the generateLayout method, we first set a bunch of values based on the application theme style. We set the Android :theme property here in the getWindowStyle method. The properties we set in the code with requestWindowFeature() are retrieved in the getLocalFeature method, This is why the requestWindowFeature() code is executed before setContentView(). The LayoutInflater then parses the layoutResource file into a View and adds it to the DecorView. The View is the PhoneWindow’s mContentRoot member variable, and the mContentParent is the layout file’s FramLayout with ID @Android: ID/Content. Back in the setContentView method, if FEATURE_CONTENT_TRANSITIONS is not set for the Window, load the layout file into the mContentParent via LayoutInflater.

ActivityThread’s handleResumeActivity() method

The Activity calls the setContentView method, which does not display the layout by itself, to activate it by calling the handleResumeActivity of the ActivityThread class. Calling the Activity’s makeVisible method in the handlerResumeActivity shows the Family of mDecor views we created above with setContentView.

final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) { ActivityClientRecord r = mActivities.get(token); . // TODO Push resumeArgs into the activity for consideration r = performResumeActivity(token, clearHide, reason); . if (r ! = null) { final Activity a = r.activity; . // If the window hasn't yet been added to the window manager, // and this guy didn't finish itself or start another activity, // then go ahead and add the window. boolean willBeVisible = ! a.mStartedActivity; if (! willBeVisible) { try { willBeVisible = ActivityManager.getService().willActivityBeVisible( a.getActivityToken()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } if (r.window == null && ! a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); ViewManager [] 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; // Add decor view objects to ViewManager 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);  }}... . // The window is now visible if it has been added, we are not // simply finishing, and we are not starting another activity. if (! r.activity.mFinished && willBeVisible && r.activity.mDecor ! = null && ! r.hideForNow) { ... r.activity.mVisibleFromServer = true; mNumVisibleActivities++; if (r.activity.mVisibleFromClient) { r.activity.makeVisible(); }}... . // Tell the activity manager we have resumed. if (reallyResume) { try { ActivityManager.getService().activityResumed(token); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); }}... . } else { // If an exception was thrown when trying to resume, then // just end this activity. try { ActivityManager.getService() .finishActivity(token, Activity.RESULT_CANCELED, null, Activity.DONT_FINISH_TASK_WITH_ACTIVITY); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); }}}Copy the code

Wm is an instance of Windows ManagerImpl, which adds decor to ViewRootImpl by calling addView() :

WindowManagerGlobal.java private final ArrayList<View> mViews = new ArrayList<View>(); private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>(); private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>(); Private static WindowManagerGlobal sDefaultWindowManager; // Private static WindowManagerGlobal sDefaultWindowManager; public static WindowManagerGlobal getInstance() { synchronized (WindowManagerGlobal.class) { if (sDefaultWindowManager == null) { sDefaultWindowManager = new WindowManagerGlobal(); } return sDefaultWindowManager; }}... . public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ... final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params; ViewRootImpl root; View panelParentView = null; synchronized (mLock) { .... . // Instantiate ViewRootImpl root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); // Add a view to the collection mviews.add (view); mRoots.add(root); mParams.add(wparams); . // Do this last because it fires off messages to start doing things try {// add view to ViewRootImpl wparams, panelParentView); } catch (RuntimeException e) { .... }}}Copy the code

The View drawing process is done by ViewRootImpl.

Android Hardware Acceleration

Since Android 4.+, hardware acceleration has been enabled and enabled by default. Some phones support hardware acceleration, but some apis do not support hardware acceleration. Such as Canvas clipPath. Hardware acceleration, intuitively speaking, depends on GPU to accelerate graphics drawing. The difference between software drawing and hardware drawing mainly lies in whether graphics drawing is processed by GPU or CPU. If it is GPU, it is regarded as hardware accelerated drawing, whereas software drawing. Hardware acceleration may directly allocate memory from the hardware buffer on the FrameBuffer (the same principle as SurfaceFlinger). Both are drawn at the APP end, and SurfaceFlinger also needs to be notified to synthesize after drawing. For Android APP, hardware accelerated drawing based on GPU can be divided into the following stages:

  • The first stage: APP constructs the commands and data required for OpenGL rendering in the UI thread
  • The second stage: THE CPU will upload (share or copy) the data to the GPU. There is generally video memory on PC, but the memory of embedded devices like ARM is generally GPU and CPU shared memory
  • Stage 3: Notice GPU rendering, in general, the real machine will not block until the GPU rendering is finished, which is inefficient, and the CPU will return to continue executing other tasks after the notification is finished. Of course, theoretically, it can also block execution, and glFinish can meet such requirements. (Fence assists GPU CPU synchronization)
  • Stage 4: swapBuffers and inform SurfaceFlinger of layer composition
  • Stage 5: SurfaceFlinger begins to compose layers, if the GPU rendering task submitted before is not finished, wait for the GPU rendering to complete, then compose (Fence mechanism), composition still depends on GPU, but this is the next task

       

A Fence is a Fence, and its role is very similar to its name. A group of threads can collectively synchronize with each other using a fence; In essence, each thread calls the wait() method of the fence when it reaches some known state, blocking until all the other participating threads invoke wait() to indicate that they have reached that state as well. Once all threads have reached the fence, they collectively unblock and continue executing together; The state that causes the program to block by calling the fence’s wait() method is called the fence state.

In Android, GraphicBuffer synchronization mainly relies on the Fence synchronization mechanism, which can handle the synchronization between GPU, CPU and HWC. Since GPU processing is generally asynchronous, when we call OpenGL API and return, the OpenGL command is not executed by GPU immediately, but cached in the local GL command buffer. When the buffer is full, the GPU will be notified to execute, and the CPU may not know the execution time at all. Unless the CPU actively uses glFinish() to force the refresh, blocking to wait for these commands to finish executing, however, there is no doubt that this will make CPU/GPU parallel processing less efficient, at least the rendering thread is blocked there; Relatively speaking, the efficiency of asynchronous processing is higher. The CPU submits the command and returns without waiting for the GPU to finish processing, so that the rendering thread is freed to process the next message. However, at this time, the graphics are submitted to SurfaceFlinger graphics composition before the processing is completed. So the SurfaceFlinger needs to know when the GraphicBuffer has been filled by the GPU processing, and that’s where the Fence mechanism comes into play.

The role of the RenderThread

Prior to Android 5.0, the main thread of an Android application was also an Open GL thread. However, since Android 5.0, the Open GL threads for Android applications have been separated into separate threads called Render Threads.

RenderThread is a thread that is managed by the system. RenderThread plays animation more smoothly than the UI thread. When hardware acceleration is enabled, Android uses the Display List component to draw each frame instead of using the CPU directly. Display List is a record of a series of draw operations abstracted into the RenderNode class.

The Display List can be drawn as many times as needed without interacting with the business logic. Specific drawing operations (such as translation, scale, etc.) can be applied to the entire Display List without redistributing the drawing operations. Once you know all the drawing operations, you can optimize them: for example, all the text can be drawn at once, and processing of the display list can be moved to another (non-UI) thread, which happens to be the responsibility of the RenderThread: Perform optimization operations outside the UI thread and distribute drawing operations to the GPU.

Before Lollipop, it was almost impossible to animate View properties (such as transition between activities) while performing redo operations. For Lollipop and Android versions above, these animations and other effects (such as Ripple) can be smoothly executed with the help of RenderThread.

The real executor of rendering is the GPU, and the GPU knows nothing about animation: The only way to execute an animation is to distribute the different draw operations for each frame to the GPU, but this logic itself cannot be executed on the GPU and if this operation is performed on the UI thread, any redo operation will block new draw instructions from being delivered in time, and the animation will be delayed.

As mentioned earlier, RenderThread can handle some parts of the display List process, but note that display List creation and modification still need to be performed in the UI thread. After the DrawOp tree is built, The UI thread sends a DrawFrameTask request to the RenderThread thread using RenderProxy, and the RenderThread wakes up and starts rendering. The general process is as follows:

  • First, merge the DrawOp
  • And then draw the special Layer
  • Finally, all the remaining drawoplists are drawn
  • The call to swapBuffers submits the previously drawn graphic buffers to Surface Flinger for synthesis and display.