We typically embed WebView in the app's UI for dynamic updates of certain features. Prior to version 4.4, Android WebView was implemented based on WebKit. However, after the 4.4 release, the Android WebView was replaced with a chromium-based implementation. Based on the chromium implementation, WebView can display Web pages faster and more smoothly. This article next introduces the implementation principles of Android WebView based on chromium, as well as the development of learning plans.
By learning from the previous series of articles, we know that the implementation of chromium is quite complex. This complexity can be reflected in the compilation of the dynamic library size, with the symbol version of about 1.3G, remove the symbol and then 27M or so. Compiled AOSP source know that throughout the compilation process, the Chromium library occupies most of the time, especially in the production of the above 1.3G file, the computer is almost stuck. Therefore, when using chromium to implement WebView, the first problem to be solved is its dynamic library loading problem.
The dynamic library of the Android system is the ELF file structure. The elf file consists of the ELF header, Program Header table, section, and section Header table. The ELF header, located at the beginning of the file, describes the offset position of the Program Header table and section Header table in the file. Section Header table describes which sections are part of a dynamic library, and a typical section has. text,. Data,. BSS, and. Rodata. These sections describe either the program code or the program data. Program Header table describes which segment the dynamic library consists of. A segment is made up of one or several sections. Section information is used during program linking, and segment information is used during program loading. For a more detailed description of the elf file format, refer to executable and linkable format.
For a dynamic library, its program code is read-only. This means that a dynamic library, regardless of how many processes are loaded, has only one copy of its program code. However, the dynamic library of program data, the linker at the time of loading, each process will be loaded independently of a copy. For the Chromium Dynamic Library, its program code occupies about 95% of the size, that is, about 25.65M. The remaining 5%, or 1.35M, is the program data. Assuming that there are N apps that use WebView, and that the N-app process exists in the system, then the system needs to allocate (25.65 + 1.35xN) m memory for the chromium dynamic library. This means that the larger the N, the more system memory the chromium dynamic library occupies.
In the Chromium dynamic library 1.35M program data, about 1.28M after the load after a relocation operation will not be changed. This data contains the C + + virtual function table, as well as constants for all pointer types. They are placed by the linker in a section called Gnu_relro, which is shown in 1:
Figure 1 App process does not share GNU_RELRO section
If we consider the GNU_RELRO section of the chromium dynamic library as normal program data, then the Android system needs to allocate 1.28M of memory for each app process that uses WebView. This will cause a waste of memory. If we can share the GNU_RELRO section of the chromium dynamic library between app processes, then no matter how many app processes use WebView, the memory size it consumes is shown in 1.28m,2:
Figure 2 sharing Gnu_relro section between app processes
This is possible, after all, it is similar to the program code and is read-only during runtime. The difference is that the program code does not have to be modified from start to finish, while the contents of the GNU_RELRO section are modified during relocation, and then read-only. However, when modified, the cow (Copy on Write) mechanism of Linux makes it impossible to share between the various app processes.
To enable the Chromium dynamic library to be shared between different app processes, the Android system performs the following actions:
1. During the boot process, the zygote process retains a virtual address within the Chromium dynamic library that is sufficient to load it. We assume that the starting address for this virtual address space is greservedaddress and the size is greservedsize.
2. During the boot process, the system process requests the zygote process to fork a child process, and the reserved virtual address space above [greservedaddress, Greservedaddress + greservedsize) Load the chromium dynamic library. The program header of the Chromium dynamic library specifies the loading location of its GNU_RELRO section. This position is an offset relative to the base address greservedaddress. We assume that this offset is grelrooffset. After the sub-process has completed the relocation of the GNU_RELRO section of the Chromium dynamic library, its contents are written to a RELRO file.
3. When the app process is created WebView, the Android system also loads the chromium dynamic library in the reserved virtual address space [greservedaddress, greservedaddress + greservedsize) above. The Relro file memory that is obtained from step 2nd is mapped directly to the virtual address space [greservedaddress + grelrooffset, greservedaddress + Grelrooffset + 1.28).
For the zygote process, system process and app process startup process, you can refer to the Android system process zygote boot process source code analysis and the Android application process startup process Source code Analysis of these two articles.
ANDROID 5.0 Linker provides a new dynamic library load function Android_dlopen_ext. This function can not only load a dynamic library in the specified virtual address space, but also can write the contents of its GNU_RELRO section to the specified RELRO file when the dynamic library relocation operation is complete, while also loading a dynamic library. Uses the specified RELRO file memory map for its GNU_RELRO section. Thus, steps 2nd and 3rd above can be achieved. The function Android_dlopen_ext has another powerful feature that reads the dynamic library content to be loaded from a specified file descriptor. Usually we specify the dynamic library to load through the file path, and with the function android_dlopen_ext, we are no longer restricted from loading the dynamic library from the local file.
Next, we further analyze why the above 3 actions allow the Chromium dynamic library to be shared between different app processes.
First, the 2nd step of the sub-process, the 3rd step of the app process is the 1th step of the zygote process fork out, so they have a reserved virtual address space [greservedaddress, greservedaddress + greservedsize).
Second, the 2nd step of the sub-process and the 3rd step of the app process is to load the Chromium dynamic library in the same virtual address space, so you can use the former generated RELRO file memory mapped to the latter Gnu_relro section.
Third, all app processes that use the WebView map the same RELRO file memory to the GNU_RELRO section of the chromium dynamic library that they load, so they are shared.
After the app process loads the Chromium dynamic library, you can start the Chromium rendering engine. The chromium rendering engine is actually made up of browser processes, render processes, and GPU processes. Where the browser process is responsible for compositing the UI of the Web page on the screen, the render process is responsible for loading and rendering the Web page of the UI,GPU process responsible for executing the browser process and GPU commands emitted by the render process. Since chromium also supports a single-process architecture (in this case, its browser process, render process, and GPU process are simulated by threading), we will then refer to its browser process, render process, and GPU processes collectively as browser, Render end and GPU side. Accordingly, the chromium rendering engine is launched to launch its browser, Render, and GPU ends.
For Android WebView, it starts with procedure 3 of the Chromium rendering engine:
Figure 3 Android WebView The process of starting the chromium rendering engine
When we embed a webview in the app's UI, a Webviewchromium object is created inside WebView. As can be seen from the name, Webviewchromium is based on the implementation of chromium WebView. There is a Android_webview module inside the chromium. This module provides two classes of awbrowserprocess and awcontents, respectively, to encapsulate the two classes Browserstartupcontroller and Contentviewcore provided by the content layer of chromium.
From the previous chromium hardware accelerated rendering of OpenGL context drawing surface creation process Analysis The article can be known, through the chromium content layer provided by the Browserstartupcontroller class, You can start the browser side of the chromium. However, for Android WebView, it does not have a separate browser process, and its browser end is implemented through the main thread of the app process (i.e. the UI thread).
The content layer of chromium launches a browser Main Loop through the Browserstartupcontroller class in the UI thread of the app process. A task can be sent to this browser Main loop when the browser side of the chromium needs to be requested to perform an operation. This browser Main loop will eventually execute the requested task in the UI thread of the app process.
From the previous Chromium Web page frame tree Create process analysis article to know that through the Chromium content layer provides the Contentviewcore class, you can create a render process, and in this render process to load the specified Web page. However, for Android WebView, it is a single-process architecture, which means that it does not have a separate render process for loading Web pages. This time the Contentviewcore class will create a thread to simulate the render process. In other words, the render side of Android WebView is implemented by a thread created in the app process. This thread is called the in-process Renderer thread.
Now, Android WebView has a browser and render side, and it needs to have a GPU side. From the previous chromium GPU process START Process Analysis article can be known that on the Android platform, the chromium GPU side is achieved by creating a GPU thread in the browser process. However, for Android WebView, it does not have a single GPU thread. So who is the chromium GPU command to execute?
We know that starting with Android 3.0, the app's UI supports hardware-accelerated rendering, which is supported by the GPU. By default, the UI for Android 4.0,app is hardware accelerated rendering. At this point the app's UI thread is an OpenGL thread, which can be used to execute GPU commands. The UI thread to Android 5.0,app is only responsible for collecting UI rendering operations, which is to build a display List. The operation that is saved in this display list will eventually be handed to a render thread for execution. The render thread is also performing these rendering operations through the GPU. The UI thread of the app is no longer an OpenGL thread, and the Render thread is. The GPU command for Android WebView is executed by this render thread. Of course, Android WebView's GPU commands were executed by the app's UI thread at Android 4.4. Here we only discuss the situation of Android 5.0, specifically refer to the Android application UI hardware accelerated rendering technology brief introduction and learn to plan this series of articles.
The chromium provides a deferredgpucommandservice service in the Android_webview module. When the browser and render sides of the Android WebView need to execute GPU commands, they will pass the REQUESTPROCESSGL interface provided by the Deferredgpucommandservice service to the app's display List adds an operation of type Drawfunctorop. When the display list is synchronized from the UI thread to the render thread, the Drawfunctorop operations contained within it are executed. At this point the Android WebView's browser and render side GPU commands are executed in the app's render thread.
Chromium provides an interface called In-process command Buffer GL for the unique GPU command execution of Android WebView (neither executed in a separate GPU process nor executed in a separate GPU thread). As the name implies, in-process command buffer GL is the same as the command buffer GL interface, and the GPU commands to be executed are written to a command buffer first. However, this command buffer will be presented to the app's render thread process execution, as shown in procedure 4:
Figure 4 Android WebView process of performing GPU commands via in-process command Buffer GL Interface
The in-process command Buffer GL interface is the same as the command buffer GL Interface analyzed in the previous chromium hardware accelerated OpenGL command execution process analysis. are described by the Gles2implementation class, except that the former performs GPU commands through a Inprocesscommandbuffer object and the latter executes GPU commands from a Commandbufferproxyimpl object. More specifically, the Commandbufferproxyimpl object will submit the GPU command to be executed to Chromium's GPU process/thread processing, Instead, the Inprocesscommandbuffer object submits the GPU command that will be executed to the app's render thread processing.
The Gles2implementation class is a member function of the Inprocesscommandbuffer class that flush requests that it handle GPU commands that have been written in command buffer. The member function of the Inprocesscommandbuffer class flush will also request the Deferredgpucommandservice service mentioned earlier to dispatch a task. This task binds the member function Flushongputhread of the Inprocesscommandbuffer class, meaning that the app's render The thread thread calls the member function Flushongputhread of the Inprocesscommandbuffer class. The member function of the Inprocesscommandbuffer class Flushongputhread the GPU commands that have been written to command buffer during the call.
After the Deferredgpucommandservice service receives a request to dispatch a task, it determines whether the current thread is allowed to perform a GL operation, and in effect determines whether the current thread is the render thread of the app. If so, the member function Flushongputhread of the Inprocesscommandbuffer class of the task binding that requested the dispatch is called directly. This happens in the process of compositing the Web UI on the browser side. The operation of the browser-side compositing Web UI is initiated by the app's render thread, so this compositing operation can be executed directly in the current thread.
On the other hand, after the Deferredgpucommandservice service receives a request to dispatch a task, it will save the task in an internal task queue if it finds that it is not currently allowed to perform a GL operation. And by calling the Java layer's member function of the Drawglfunctor class REQUESTDRAWGL request the app's UI thread to schedule a GL operation to execute. This happens in the process of rendering the Web UI on the render side. From the previous Chromium web page rendering mechanism brief introduction and Learning plan this series of articles can be known that the render end renders the Web UI operation in an internal compositor thread. This compositor thread is not an OpenGL thread, it cannot directly perform the operation of GL, so it is necessary to request the app's UI thread to schedule the execution of the GL operation.
From the front of the Android application UI hardware accelerated rendering technology brief introduction and Learning plan this series of articles can be learned that the app's UI thread is primarily responsible for collecting rendering operations for the current UI, and write these rendering operations to a display list through a displaylistrenderer. The display list will eventually be synced to the app's render thread. After the app's render thread obtains the display list, it performs these rendering operations by invoking the appropriate OpenGL functions, which are performed by the GPU. This process is called Replay (replay) Display List. Once the replay is complete, the app's UI can be generated.
The member function of the Drawglfunctor class REQUESTDRAWGL request the app's UI thread to schedule the execution of the GL operation is a special rendering operation, which corresponds to a GL function, while the general rendering operation refers to drawing a circle, Basic operations such as Rect or bitmap. During the execution of the GL function, arbitrary GPU commands can be executed. The UI thread of the app calls the member function of the Displaylistrenderer class Calldrawglfunction encapsulates the GL action of the request schedule execution into a DRAWFUNCTIONOP operation, and saves the display in the app UI List. When the display list is replayed by the app's render thread, the DRAWFUNCTIONOP operation contained in it is executed calldrawglfunction by the member function of the Openglrenderer class.
The member function of the Openglrenderer class calldrawglfunction a GL function implemented in the Android_webview module of chromium when performing DRAWFUNCTIONOP operations. This GL function will also find a Hardwarerenderer object. This Hardwarerenderer object is the browser end of the Android WebView to compose the UI of the Web page. With this Hardwarerenderer object, the GL function above calls its member function DRAWGL to request the Deferredgpucommandservice service mentioned earlier to execute the task stored inside it. This is implemented by calling its member function Performidletask.
The member functions of the Deferredgpucommandservice class Performidletask each task that is saved in the internal task queue in turn. As you can see from the previous analysis, there is a task in the task queue. This task binds the member function Flushongputhread of the Inprocesscommandbuffer class. Therefore, this time the member function of the Inprocesscommandbuffer class Flushongputhread is called in the current thread, which is called in the app's render thread.
As mentioned earlier, the member function of the Inprocesscommandbuffer class Flushongputhread in the process of the call, before it is executed by in-process command Buffer, the GL interface is written in the command The GPU command in buffer. The member function of the Inprocesscommandbuffer class Flushongputhread is also performed by an internal Gpuscheduler object and a Gles2decoder object that performs these GPU commands. The Gpuscheduler object is responsible for scheduling GPU commands to execute in their corresponding OpenGL contexts, while the Gles2decoder object is responsible for translating GPU commands into OpenGL function execution. For the process of performing GPU commands on the Gpuscheduler class and the Gles2decoder class, you can refer to the previous OpenGL commands for chromium hardware accelerated rendering to perform the process analysis article.
By the way described above, the Android WebView can execute arbitrary GPU commands on the browser and render sides. Where the render side executes the GPU command to render the UI of the Web page, and the browser side executes the GPU command to synthesize the Web UI rendered on the render side into the UI of the app so that it can finally be displayed on the screen. This process is the process of the Android WebView hardware to accelerate the rendering of web pages, as shown in 5:
Figure 5 Android WebView Hardware Accelerated rendering Web page process
From the front of the Android application UI hardware accelerated rendering technology brief introduction and Learning plan this series of articles can be known that the app starts rendering a frame UI from receiving the VSync signal. Two vsync signal intervals are determined by the screen refresh rate. The average screen refresh rate is 60fps, which means that every vsync signal arrives, the app has 16ms of time to draw its own UI.
This 16ms time is divided into three stages. The first stage is the display List used to construct the app UI. This construction work is performed by the app's UI thread, which is represented as the member function of each view that needs to be updated OnDraw will be called. The UI thread for the second-stage app synchronizes the previously constructed display list to the render thread. This synchronization is performed by the app's render thread, and the app's UI thread is blocked until the sync operation is complete. The third stage is the display List that draws the app UI. This drawing operation is performed by the app's render thread.
Since Android WebView is embedded in the app's UI, the rendering of each frame is performed in the three stages described above. From the previous Chromium web page rendering mechanism brief introduction and Learning plan this series of articles can be known that the UI of a Web page is ultimately described by a CC layer tree, that is, the render side of Android WebView will create a CC layer Tree to describe the UI of the Web page it is loading. At the same time, Android WebView also creates a synchronous compositor for the render end to render the UI of the Web page on a synchronous compositor Output surface. The final result of rendering is described by a compositor frame. This means that each frame of the page is described by a compositor frame.
The browser side of the Android WebView is responsible for crafting the rendered UI of the render side. To complete this compositing operation, it also creates a CC Layer Tree. This cc Layer tree has only two nodes, one is the root node and the other is a child node of the root node, called the delegated Renderer layer. The content of this delegated Renderer layer is derived from the render end rendering, which is a compositor Frame. At the same time, Android WebView will create a hardware Renderer for the browser side to render its cc Layer tree on a parent Output surface. In fact, the UI of the Web page is synthesized in the app's window.
In the first stage, the member function of Android WebView OnDraw is called in the UI thread of the app. During the call, it calls the member function DEMANDDRAWHW of the synchronous compositor created for the render side to render the CC Layer Tree of the render side. After rendering is complete, the member function of the synchronous compositor Output surface created for the render side swapbuffers is called and handed to it as a compositor Frame. This compositor frame describes the current UI of the Web page, which is eventually saved in a Sharedrendererstate object. This Sharedrendererstate object is used to describe the state of a Web page.
In the second stage, the compositor frame stored in the above Sharedrendererstate object is extracted and synchronized to the browser side of the CC layer tree, which is the delegated set to the CC layer tree Renderer the content of the layer.
In the third stage, the hardware renderer member function DRAWGL created for the browser end of Android WebView is called in the app's render thread to render the CC Layer Tree on the browser side. Once rendered, the member function swapbuffers of the parent Output surface created for the browser end is called, and it draws the resulting web page UI to the app's window.
The above is the process by which Android WebView renders the Web UI in the app window in a hardware-accelerated fashion. Of course, Android WebView can also use software to render Web UI in the app window. However, in this series of articles, we just focus on hardware-accelerated rendering, because this rendering is more efficient, and the Android system from the 4.0 version, the default is to use hardware-accelerated rendering of the app's UI.
Next, we will combine the source code, according to the following four scenarios, detailed analysis of the Android WebView hardware accelerated rendering Web UI process:
1. Android WebView The process of loading chromium dynamic library;
2. Android WebView The process of starting the chromium rendering engine;
3. Android WebView The process of executing OpenGL commands;
4. Android WebView Hardware accelerates the process of rendering Web UI.
After analyzing these four scenarios, we can have a deep understanding of the implementation of Android WebView. Please pay attention! More information can also focus on Lao Luo's Sina Weibo: Http://weibo.com/shengyangluo.
Android WebView Brief introduction and Learning Plan