Android WebView加载Chromium动态库的过程分析



http://blog.csdn.net/luoshengyang/article/details/53209199


       Chromium动态库的体积比较大,有27M左右,其中程序段和数据段分别占据25.65M和1.35M。如果按照通常方式加载Chromium动态库,那么当有N个正在运行的App使用WebView时,系统需要为Chromium动态库分配的内存为(25.65 + N x 1.35)M。这是非常可观的。为此,Android使用了特殊的方式加载Chromium动态库。本文接下来就详细分析这种特殊的加载方式。

老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!

《Android系统源代码情景分析》一书正在进击的程序员网(http://0xcc0xcd.com)中连载,点击进入!

       为什么当有N个正在运行的App使用WebView时,系统需要为Chromium动态库分配的内存为(25.65 + N x 1.35)M呢?这是由于动态库的程序段是只读的,可以在多个进程之间进行共享,但是数据段一般是可读可写的,不能共享。在1.35M的数据段中,有1.28M在Chromium动态库加载完成后就是只读的。这1.28M数据包含有C++虚函数表,以及指针类型的常量等,它们在编译的时候会放在一个称为GNU_RELRO的Section中,如图1所示:


图1 App进程间不共享GNU_RELRO Section

       如果我们将该GNU_RELRO Section看作是一般的数据段,那么系统就需要为每一个使用了WebView的App进程都分配一段1.28M大小的内存空间。前面说到,这1.28M数据在Chromium动态库加载完成后就是只读的,那么有没有办法让它像程序段一样,在多个App进程之间共享呢?

       只要能满足一个条件,那么答案就是肯定的。这个条件就是所有的App进程都在相同的虚拟地址空间加载Chromium动态库。在这种情况下,就可以在系统启动的过程中,创建一个临时进程,并且在这个进程中加载Chromium动态库。假设Chromium动态库的加载地址为Base Address。加载完成后,将Chromium动态库的GNU_RELRO Section的内容Dump出来,并且写入到一个文件中去。

       以后App进程加载Chromium动态库时,都将Chromium动态库加载地址Base Address上,并且使用内存映射的方式将前面Dump出来的GNU_RELRO文件代替Chromium动态库的GNU_RELRO Section。这样就可以实现在多个App进程之间共享Chromium动态库的GNU_RELRO Section了,如图2所示:


图2 App进程间共享GNU_RELRO Section

       从前面Android应用程序进程启动过程的源代码分析一文可以知道,所有的App进程都是由Zygote进程fork出来的,因此,要让所有的App进程都在相同的虚拟地址空间加载Chromium动态库的最佳方法就是在Zygote进程的地址空间中预留一块地址。

       还有两个问题需要解决。第一个问题是要将加载后的Chromium动态库的GNU_RELRO Section的内容Dump出来,并且写入到一个文件中去。第二个问题是要让所有的App进程将Chromium动态加载在指定位置,并且可以使用指定的文件来代替它的GNU_RELRO Section。这两个问题都可以通过Android在5.0版本提供的一个动态库加载函数android_dlopen_ext解决。

       接下来,我们就结合源码,分析Zygote进程是如何为App进程预留地址加载Chromium动态库的,以及App进程如何使用新的动态库加载函数android_dlopen_ext加载Chromium动态库的。

       从前面Android系统进程Zygote启动过程的源代码分析一文可以知道,Zygote进程在Java层的入口函数为ZygoteInit类的静态成员函数main,它的实现如下所示:

[java]   view plain  copy
  1. public class ZygoteInit {  
  2.     ......  
  3.   
  4.     public static void main(String argv[]) {  
  5.         try {  
  6.             .......  
  7.   
  8.             preload();  
  9.             .......  
  10.   
  11.             if (startSystemServer) {  
  12.                 startSystemServer(abiList, socketName);  
  13.             }  
  14.   
  15.             ......  
  16.             runSelectLoop(abiList);  
  17.   
  18.             ......  
  19.         } catch (MethodAndArgsCaller caller) {  
  20.             ......  
  21.         } catch (RuntimeException ex) {  
  22.             ......  
  23.         }  
  24.     }  
  25.   
  26.     ......  
  27. }  

       这个函数定义在文件frameworks/base/core/java/com/android/internal/os/ZygoteInit.java中。

       ZygoteInit类的静态成员函数main在启动System进程以及使得Zygote进程进入运行状态之前,首先会调用另外一个静态成员函数preload预加载资源。这些预加载的资源以后就可以在App进程之间进行共享。

       ZygoteInit类的静态成员函数preload在预加载资源的过程中,就会为Chromium动态库预保留加载地址,如下所示:

[java]   view plain  copy
  1. public class ZygoteInit {  
  2.     ......  
  3.   
  4.     static void preload() {  
  5.         ......  
  6.         // Ask the WebViewFactory to do any initialization that must run in the zygote process,  
  7.         // for memory sharing purposes.  
  8.         WebViewFactory.prepareWebViewInZygote();  
  9.         ......  
  10.     }  
  11.   
  12.     ......  
  13. }  

       这个函数定义在文件frameworks/base/core/java/com/android/internal/os/ZygoteInit.java中。

       ZygoteInit类的静态成员函数preload是通过调用WebViewFactory类的静态成员函数prepareWebViewInZygote为Chromium动态库预保留加载地址的,后者的实现如下所示:

[java]   view plain  copy
  1. public final class WebViewFactory {  
  2.     ......  
  3.   
  4.     public static void prepareWebViewInZygote() {  
  5.         try {  
  6.             System.loadLibrary("webviewchromium_loader");  
  7.             long addressSpaceToReserve =  
  8.                     SystemProperties.getLong(CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY,  
  9.                     CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES);  
  10.             sAddressSpaceReserved = nativeReserveAddressSpace(addressSpaceToReserve);  
  11.   
  12.             ......  
  13.         } catch (Throwable t) {  
  14.             ......  
  15.         }  
  16.     }  
  17.   
  18.     ......  
  19. }  

       这个函数定义在文件frameworks/base/core/java/android/webkit/WebViewFactory.java中。

       WebViewFactory类的静态成员函数prepareWebViewInZygote首先会加载一个名称为“webviewchromium_loader”的动态库,接下来又会获得需要为Chromium动态库预留的地址空间大小addressSpaceToReserve。知道了要预留的地址空间的大小之后,WebViewFactory类的静态成员函数prepareWebViewInZygote就会调用另外一个静态成员函数nativeReserveAddressSpace为Chromium动态库预留地址空间。

       WebViewFactory类的静态成员函数nativeReserveAddressSpace是一个JNI方法,它在C++层对应的函数为ReserveAddressSpace。这个函数实现在上述名称为“webviewchromium_loader”的动态库中,如下所示:

[cpp]   view plain  copy
  1. jboolean ReserveAddressSpace(JNIEnv*, jclass, jlong size) {  
  2.   return DoReserveAddressSpace(size);  
  3. }  

       这个函数定义在文件frameworks/webview/chromium/loader/loader.cpp中。

       函数ReserveAddressSpace调用另外一个函数DoReserveAddressSpace预留大小为size的地址空间,如下所示:

[cpp]   view plain  copy
  1. void* gReservedAddress = NULL;  
  2. size_t gReservedSize = 0;  
  3.   
  4. jboolean DoReserveAddressSpace(jlong size) {  
  5.   size_t vsize = static_cast<size_t>(size);  
  6.   
  7.   void* addr = mmap(NULL, vsize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);  
  8.   ......  
  9.   
  10.   gReservedAddress = addr;  
  11.   gReservedSize = vsize;  
  12.   ......  
  13.   
  14.   return JNI_TRUE;  
  15. }  

       这个函数定义在文件frameworks/webview/chromium/loader/loader.cpp中。

       函数DoReserveAddressSpace是通过系统接口mmap预留指定大小的地址空间的。同时,预留出来的地址空间的起始地址和大小分别记录在全局变量gReservedAddress和gReservedSize中。以后Chromium动态库就可以加载在该地址空间中。

       为Chromium动态库预留好加载地址之后,Android系统接下来要做的一件事情就是请求Zygote进程启动一个临时进程。这个临时进程将会在上述预留的地址空间中加载Chromium动态库。这个Chromium动态库在加载的过程中,它的GNU_RELRO Section会被重定位。重定位完成之后,就可以将它的内容Dump到一个文件中去。这个文件以后就会以内存映射的方式代替在App进程中加载的Chromium动态库的GNU_RELRO Section。

       请求Zygote进程启动临时进程的操作是由System进程完成的。从前面Android系统进程Zygote启动过程的源代码分析一文可以知道,System进程是由Zygote进程启动的,它在Java层的入口函数为SystemServer的静态成员函数main,它的实现如下所示:

[java]   view plain  copy
  1. public final class SystemServer {  
  2.     ......  
  3.   
  4.     public static void main(String[] args) {  
  5.         new SystemServer().run();  
  6.     }  
  7.   
  8.     ......  
  9. }  

      这个函数定义在文件frameworks/base/services/java/com/android/server/SystemServer.java中。

      SystemServer的静态成员函数main首先是创建一个SystemServer对象,然后调用这个SystemServer对象的成员函数run在System进程中启动各种系统服务,如下所示:

[java]   view plain  copy
  1. public final class SystemServer {  
  2.     ......  
  3.   
  4.     private void run() {  
  5.         ......  
  6.   
  7.         // Start services.  
  8.         try {  
  9.             startBootstrapServices();  
  10.             startCoreServices();  
  11.             startOtherServices();  
  12.         } catch (Throwable ex) {  
  13.             ......  
  14.         }  
  15.   
  16.         ......  
  17.   
  18.         // Loop forever.  
  19.         Looper.loop();  
  20.         throw new RuntimeException("Main thread loop unexpectedly exited");  
  21.     }  
  22.   
  23.     ......  
  24. }  

       这个函数定义在文件frameworks/base/services/java/com/android/server/SystemServer.java中。

       SystemServer类的成员函数run首先会调用成员函数startBootstrapServices启动Bootstrap类型的系统服务。这些系统服务包括Activity Manager Service、Power Manager Service、Package Manager Service和Display Manager Service等。

       SystemServer类的成员函数run接下来又会调用成员函数startCoreServices启动Core类型的系统服务。这些系统服务包括Lights Service、Battery Service和Usage Stats Service等。

       SystemServer类的成员函数run再接下来还会调用成员函数startOtherServices启动其它的系统服务。这些其它的系统服务包括Account Manager Service、Network Management Service和Window Manager Service等。

       SystemServer类的成员函数startOtherServices启动完成其它的系统服务之后,就会创建一个Runnable。这个Runnable会在Activity Manger Service启动完成后执行,如下所示:

[java]   view plain  copy
  1. public final class SystemServer {  
  2.     ......  
  3.   
  4.     private void startOtherServices() {  
  5.         final Context context = mSystemContext;  
  6.         AccountManagerService accountManager = null;  
  7.         ContentService contentService = null;  
  8.         VibratorService vibrator = null;  
  9.         IAlarmManager alarm = null;  
  10.         MountService mountService = null;  
  11.         NetworkManagementService networkManagement = null;  
  12.         NetworkStatsService networkStats = null;  
  13.         NetworkPolicyManagerService networkPolicy = null;  
  14.         ConnectivityService connectivity = null;  
  15.         NetworkScoreService networkScore = null;  
  16.         NsdService serviceDiscovery= null;  
  17.         WindowManagerService wm = null;  
  18.         BluetoothManagerService bluetooth = null;  
  19.         UsbService usb = null;  
  20.         SerialService serial = null;  
  21.         NetworkTimeUpdateService networkTimeUpdater = null;  
  22.         CommonTimeManagementService commonTimeMgmtService = null;  
  23.         InputManagerService inputManager = null;  
  24.         TelephonyRegistry telephonyRegistry = null;  
  25.         ConsumerIrService consumerIr = null;  
  26.         AudioService audioService = null;  
  27.         MmsServiceBroker mmsService = null;  
  28.         ......  
  29.   
  30.        mActivityManagerService.systemReady(new Runnable() {  
  31.             @Override  
  32.             public void run() {  
  33.                 ......  
  34.   
  35.                 WebViewFactory.prepareWebViewInSystemServer();  
  36.   
  37.                 ......  
  38.             }  
  39.         });  
  40.     }  
  41.   
  42.     ......  
  43. }  

       这个函数定义在文件frameworks/base/services/java/com/android/server/SystemServer.java中。

       这个Runnable在执行的时候,会调用WebViewFactory类的静态成员函数prepareWebViewInSystemServer请求Zygote进程启动一个临时的进程,用来加载Chromium动态库,并且将完成重定位操作后的GNU_RELRO Section的内容Dump到一个文件中去。

       WebViewFactory类的静态成员函数prepareWebViewInSystemServer的实现如下所示:

[java]   view plain  copy
  1. public final class WebViewFactory {  
  2.     ......  
  3.   
  4.     public static void prepareWebViewInSystemServer() {  
  5.         String[] nativePaths = null;  
  6.         try {  
  7.             nativePaths = getWebViewNativeLibraryPaths();  
  8.         } catch (Throwable t) {  
  9.             // Log and discard errors at this stage as we must not crash the system server.  
  10.             Log.e(LOGTAG, "error preparing webview native library", t);  
  11.         }  
  12.         prepareWebViewInSystemServer(nativePaths);  
  13.     }  
  14.   
  15.     ......  
  16. }  

       这个函数定义在文件frameworks/base/core/java/android/webkit/WebViewFactory.java中。

       WebViewFactory类的静态成员函数prepareWebViewInSystemServer首先调用另外一个静态成员函数getWebViewNativeLibraryPaths获得Chromium动态库的文件路径,如下所示:

[java]   view plain  copy
  1. public final class WebViewFactory {  
  2.     ......  
  3.   
  4.     private static String[] getWebViewNativeLibraryPaths()  
  5.             throws PackageManager.NameNotFoundException {  
  6.         final String NATIVE_LIB_FILE_NAME = "libwebviewchromium.so";  
  7.   
  8.         PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();  
  9.         ApplicationInfo ai = pm.getApplicationInfo(getWebViewPackageName(), 0);  
  10.   
  11.         String path32;  
  12.         String path64;  
  13.         boolean primaryArchIs64bit = VMRuntime.is64BitAbi(ai.primaryCpuAbi);  
  14.         if (!TextUtils.isEmpty(ai.secondaryCpuAbi)) {  
  15.             // Multi-arch case.  
  16.             if (primaryArchIs64bit) {  
  17.                 // Primary arch: 64-bit, secondary: 32-bit.  
  18.                 path64 = ai.nativeLibraryDir;  
  19.                 path32 = ai.secondaryNativeLibraryDir;  
  20.             } else {  
  21.                 // Primary arch: 32-bit, secondary: 64-bit.  
  22.                 path64 = ai.secondaryNativeLibraryDir;  
  23.                 path32 = ai.nativeLibraryDir;  
  24.             }  
  25.         } else if (primaryArchIs64bit) {  
  26.             // Single-arch 64-bit.  
  27.             path64 = ai.nativeLibraryDir;  
  28.             path32 = "";  
  29.         } else {  
  30.             // Single-arch 32-bit.  
  31.             path32 = ai.nativeLibraryDir;  
  32.             path64 = "";  
  33.         }  
  34.         if (!TextUtils.isEmpty(path32)) path32 += "/" + NATIVE_LIB_FILE_NAME;  
  35.         if (!TextUtils.isEmpty(path64)) path64 += "/" + NATIVE_LIB_FILE_NAME;  
  36.         return new String[] { path32, path64 };  
  37.     }  
  38.   
  39.     ......  
  40. }  

       这个函数定义在文件frameworks/base/core/java/android/webkit/WebViewFactory.java中。

       Android系统将Chromium动态库打包在一个WebView Package中。这个WebView Package也是一个APK,它的Package Name可以通过调用WebViewFactory类的静态成员函数getWebViewPackageName获得,如下所示:

[java]   view plain  copy
  1. public final class WebViewFactory {  
  2.     ......  
  3.   
  4.     public static String getWebViewPackageName() {  
  5.         return AppGlobals.getInitialApplication().getString(  
  6.                 com.android.internal.R.string.config_webViewPackageName);  
  7.     }  
  8.   
  9.     ......  
  10. }  

       这个函数定义在文件frameworks/base/core/java/android/webkit/WebViewFactory.java中。

       WebViewFactory类的静态成员函数getWebViewPackageName又是通过系统字符串资源com.android.internal.R.string.config_webViewPackageName获得WebView Package的Package Name。

       系统字符串资源com.android.internal.R.string.config_webViewPackageName的定义如下所示:

[html]   view plain  copy
  1. <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">  
  2.   ......  
  3.   
  4.   <!-- Package name providing WebView implementation. -->  
  5.     <string name="config_webViewPackageName" translatable="false">com.android.webview</string>  
  6.   
  7.   ......  
  8. </resources>  

       这个字符串资源定义在文件frameworks/base/core/res/res/values/config.xml中。

       从这里就可以看到,WebView Package这个APK的Package Name定义为"com.android.webview"。通过查找源码,可以发现,这个APK实现在目录frameworks/webview/chromium中。

       回到WebViewFactory类的静态成员函数getWebViewNativeLibraryPaths中,它知道了WebView Package这个APK的Package Name之后,就可以通过Package Manager Service获得它的安装信息。Package Manager Service负责安装系统上所有的APK,因此通过它可以获得任意一个APK的安装信息。关于Package Manager Service,可以参考Android应用程序安装过程源代码分析这篇文章。

       获得了WebView Package这个APK的安装信息之后,就可以知道它用来保存动态库的目录。Chromium动态库就是保存在这个目录下的。因此,WebViewFactory类的静态成员函数getWebViewNativeLibraryPaths最终可以获得Chromium动态库的文件路径。注意,获得Chromium动态库的文件路径可能有两个。一个是32位版本的,另外一个是64位版本的。

       为什么要将Chromium动态库打包在一个WebView Package中呢?而不是像其它的系统动态库一样,直接放在/system/lib目录下。原因为了方便以后升级WebView。例如,Chromium有了新的版本之后,就可以将它打包在一个更高版本的WebView Package中。当用户升级WebView Package的时候,就获得了基于新版本Chromium实现的WebView了。

       这一步执行完成后,回到WebViewFactory类的静态成员函数prepareWebViewInSystemServer中,这时候它就获得了Chromium动态库的文件路径。接下来,它继续调用另外一个静态成员函数prepareWebViewInSystemServer请求Zygote进程启动一个临时的进程,用来加载Chromium动态库,以便将完成重定位操作后的GNU_RELRO Section的内容Dump到一个文件中去。

       WebViewFactory类的静态成员函数prepareWebViewInSystemServer的实现如下所示:

[java]   view plain  copy
  1. public final class WebViewFactory {  
  2.   
  3.     ......  
  4.   
  5.     private static void prepareWebViewInSystemServer(String[] nativeLibraryPaths) {  
  6.         if (DEBUG) Log.v(LOGTAG, "creating relro files");  
  7.   
  8.         // We must always trigger createRelRo regardless of the value of nativeLibraryPaths. Any  
  9.         // unexpected values will be handled there to ensure that we trigger notifying any process  
  10.         // waiting on relreo creation.  
  11.         if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {  
  12.             if (DEBUG) Log.v(LOGTAG, "Create 32 bit relro");  
  13.             createRelroFile(false /* is64Bit */, nativeLibraryPaths);  
  14.         }  
  15.   
  16.         if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {  
  17.             if (DEBUG) Log.v(LOGTAG, "Create 64 bit relro");  
  18.             createRelroFile(true /* is64Bit */, nativeLibraryPaths);  
  19.         }  
  20.     }  
  21.   
  22.     ......  
  23. }  

       这个函数定义在文件frameworks/base/core/java/android/webkit/WebViewFactory.java中。

       WebViewFactory类的静态成员函数prepareWebViewInSystemServer根据系统对32位和64位的支持情况,调用另外一个静态成员函数createRelroFile相应地创建32位和64位的Chromium GNU_RELRO Section文件。

       WebViewFactory类的静态成员函数createRelroFile的实现如下所示:

[java]   view plain  copy
  1. public final class WebViewFactory {  
  2.     ......  
  3.   
  4.     private static void createRelroFile(final boolean is64Bit, String[] nativeLibraryPaths) {  
  5.         ......  
  6.   
  7.         try {  
  8.             ......  
  9.   
  10.             int pid = LocalServices.getService(ActivityManagerInternal.class).startIsolatedProcess(  
  11.                     RelroFileCreator.class.getName(), nativeLibraryPaths, "WebViewLoader-" + abi, abi,  
  12.                     Process.SHARED_RELRO_UID, crashHandler);  
  13.             ......  
  14.         } catch (Throwable t) {  
  15.             ......  
  16.         }  
  17.     }  
  18.   
  19.     ......  
  20. }  

       这个函数定义在文件frameworks/base/core/java/android/webkit/WebViewFactory.java中。

       WebViewFactory类的静态成员函数createRelroFile请求系统的Activity Manager Service启动一个Isolated Process。这个Isolated Process的启动过程与App进程是一样的,只不过它是一个被隔离的进程,没有自己的权限。

       从前面Android应用程序进程启动过程的源代码分析一文可以知道,App进程最终是由Zygote进程fork出来的,并且它在Java层的入口函数为ActivityThread类的静态成员函数main。这个入口函数是可以指定的。WebViewFactory类的静态成员函数createRelroFile将请求启动的Isolated Process的入口函数指定为RelroFileCreator类的静态成员函数main。

       这意味着,当上述Isolated Process启动起来之后,RelroFileCreator类的静态成员函数main就会被调用,如下所示:

[java]   view plain  copy
  1. public final class WebViewFactory {  
  2.     ......  
  3.   
  4.     private static final String CHROMIUM_WEBVIEW_NATIVE_RELRO_32 =  
  5.             "/data/misc/shared_relro/libwebviewchromium32.relro";  
  6.     private static final String CHROMIUM_WEBVIEW_NATIVE_RELRO_64 =  
  7.             "/data/misc/shared_relro/libwebviewchromium64.relro";  
  8.     ......  
  9.   
  10.     private static class RelroFileCreator {  
  11.         // Called in an unprivileged child process to create the relro file.  
  12.         public static void main(String[] args) {  
  13.             ......  
  14.             try{  
  15.                 ......  
  16.                 result = nativeCreateRelroFile(args[0/* path32 */,  
  17.                                                args[1/* path64 */,  
  18.                                                CHROMIUM_WEBVIEW_NATIVE_RELRO_32,  
  19.                                                CHROMIUM_WEBVIEW_NATIVE_RELRO_64);  
  20.                 ......  
  21.             } finally {  
  22.                 ......  
  23.             }  
  24.         }  
  25.     }  
  26.   
  27.     ......  
  28. }  

       这个函数定义在文件frameworks/base/core/java/android/webkit/WebViewFactory.java中。

       前面获得的Chromium动态库文件路径会通过参数args传递进来。有了Chromium动态库文件路径之后,RelroFileCreator类的静态成员函数main就会调用另外一个静态成员函数nativeCreateRelroFile对它进行加载。加载完成后,RelroFileCreator类的静态成员函数nativeCreateRelroFile会将Chromium动态库已经完成重定位操作的Chromium GNU_RELRO Section写入到指定的文件中去。对于32位的Chromium动态库来说,它的GNU_RELRO Section会被写入到文件/data/misc/shared_relro/libwebviewchromium32.relro中,而对于64位的Chromium动态库来说,它的GNU_RELRO Section会被写入到文件/data/misc/shared_relro/libwebviewchromium64.relro中。

       RelroFileCreator类的静态成员函数nativeCreateRelroFile是一个JNI方法,它由C++层的函数CreateRelroFile实现,如下所示:

[cpp]   view plain  copy
  1. jboolean CreateRelroFile(JNIEnv* env, jclass, jstring lib32, jstring lib64,  
  2.                          jstring relro32, jstring relro64) {  
  3. #ifdef __LP64__  
  4.   jstring lib = lib64;  
  5.   jstring relro = relro64;  
  6.   (void)lib32; (void)relro32;  
  7. #else  
  8.   jstring lib = lib32;  
  9.   jstring relro = relro32;  
  10.   (void)lib64; (void)relro64;  
  11. #endif  
  12.   jboolean ret = JNI_FALSE;  
  13.   const char* lib_utf8 = env->GetStringUTFChars(lib, NULL);  
  14.   if (lib_utf8 != NULL) {  
  15.     const char* relro_utf8 = env->GetStringUTFChars(relro, NULL);  
  16.     if (relro_utf8 != NULL) {  
  17.       ret = DoCreateRelroFile(lib_utf8, relro_utf8);  
  18.       env->ReleaseStringUTFChars(relro, relro_utf8);  
  19.     }  
  20.     env->ReleaseStringUTFChars(lib, lib_utf8);  
  21.   }  
  22.   return ret;  
  23. }  

       这个函数定义在文件frameworks/webview/chromium/loader/loader.cpp中。

       函数CreateRelroFile判断自己是32位还是64位的实现,然后从参数lib32和lib64中选择对应的Chromium动态库进行加载。这个加载过程是通过调用另外一个函数DoCreateRelroFile实现的,如下所示:

[cpp]   view plain  copy
  1. void* gReservedAddress = NULL;  
  2. size_t gReservedSize = 0;  
  3.   
  4. ......  
  5.   
  6. jboolean DoCreateRelroFile(const char* lib, const char* relro) {  
  7.   ......  
  8.   
  9.   static const char tmpsuffix[] = ".XXXXXX";  
  10.   char relro_tmp[strlen(relro) + sizeof(tmpsuffix)];  
  11.   strlcpy(relro_tmp, relro, sizeof(relro_tmp));  
  12.   strlcat(relro_tmp, tmpsuffix, sizeof(relro_tmp));  
  13.   int tmp_fd = TEMP_FAILURE_RETRY(mkstemp(relro_tmp));  
  14.   ......  
  15.   
  16.   android_dlextinfo extinfo;  
  17.   extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_WRITE_RELRO;  
  18.   extinfo.reserved_addr = gReservedAddress;  
  19.   extinfo.reserved_size = gReservedSize;  
  20.   extinfo.relro_fd = tmp_fd;  
  21.   void* handle = android_dlopen_ext(lib, RTLD_NOW, &extinfo);  
  22.   int close_result = close(tmp_fd);  
  23.   ......  
  24.   
  25.   if (close_result != 0 ||  
  26.       chmod(relro_tmp, S_IRUSR | S_IRGRP | S_IROTH) != 0 ||  
  27.       rename(relro_tmp, relro) != 0) {  
  28.     ......  
  29.     return JNI_FALSE;  
  30.   }  
  31.   
  32.   return JNI_TRUE;  
  33. }  

       这个函数定义在文件frameworks/webview/chromium/loader/loader.cpp中。

       参数lib描述的是要加载的Chromium动态库的文件路径,另外一个参数relro描述的是要生成的Chromium GNU_RELRO Section文件路径。

       我们假设要加载的Chromium动态库是32位的。函数DoCreateRelroFile的执行过程如下所示:

       1.  创建一个临时的mium GNU_RELRO Section文件,文件路径为”/data/misc/shared_relro/libwebviewchromium32.relro.XXXXXX“,并且会找开这个临时文件,获得一个文件描述符tmp_fd。

       2. 创建一个android_dlextinfo结构体。这个android_dlextinfo结构体会指定一个ANDROID_DLEXT_RESERVED_ADDRESS标志,以及一个Reserved地址gReservedAddress,表示要将Chromium动态库加载在全局变量gReservedAddress描述的地址中。从前面的分析可以知道,Zygote进程为Chromium动态库预留的地址空间的起始地址就保存在全局变量gReservedAddress中。由于当前进程是由Zygote进程fork出来的,因此它同样会获得Zygote进程预留的地址空间。

       3. 前面创建的android_dlextinfo结构体,同时还会指定一个ANDROID_DLEXT_WRITE_RELRO标专,以及一个Relro文件描述符tmp_fd,表示在加载完成Chromium动态库后,将完成重定位操作后的GNU_RELRO Section写入到指定的文件中去,也就是写入到上述的临时文件/data/misc/shared_relro/libwebviewchromium32.relro.XXXXXX中去。

       4. 调用Linker导出的函数android_dlopen_ext按照上述两点规则加载Chromium动态库。加载完成后,临时文件/data/misc/shared_relro/libwebviewchromium32.relro.XXXXXX将被重新命名为”/data/misc/shared_relro/libwebviewchromium32.relro“。

       这样,我们得就到一个Chromium GNU_RELRO Section文件。这个Chromium GNU_RELRO Section文件在App进程加载Chromium动态库时就会使用到。接下来我们就继续分析App进程加载Chromium动态库的过程。

       当App使用了WebView的时候,系统就为会其加载Chromium动态库。这个加载过程是从创建WebView对象的时候发起的。因此,接下来我们就从WebView类的构造函数开始分析App进程加载Chromium动态库的过程,如下所示:

[java]   view plain  copy
  1. public class WebView extends AbsoluteLayout  
  2.         implements ViewTreeObserver.OnGlobalFocusChangeListener,  
  3.         ViewGroup.OnHierarchyChangeListener, ViewDebug.HierarchyHandler {  
  4.     ......  
  5.   
  6.     protected WebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes,  
  7.             Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {  
  8.         ......  
  9.   
  10.         ensureProviderCreated();  
  11.         mProvider.init(javaScriptInterfaces, privateBrowsing);  
  12.   
  13.         ......  
  14.     }  
  15.   
  16.     ......  
  17. }  

       这个函数定义在文件frameworks/base/core/java/android/webkit/WebView.java中。

       WebView类的构造函数会调用另外一个成员函数ensureProviderCreated确保Chromium动态库已经加载。在Chromium动态库已经加载的情况下,WebView类的成员函数ensureProviderCreated还会创建一个WebView Provider,并且保存保存在成员变量mProvider中。这个WebView Provider才是真正用来实现WebView的功能的。

      有了这个WebView Provider之后,WebView类的构造函数就会调用它的成员函数init启动网页渲染引擎。对于基于Chromium实现的WebView来说,它使用的WebView Provider是一个WebViewChromium对象。当这个WebViewChromium对象的成员函数init被调用的时候,它就会启动Chromium的网页渲染引擎。这个过程我们在接下来的一篇文章再详细分析。

       接下来,我们继续分析WebView类的成员函数ensureProviderCreated的实现,如下所示:

[java]   view plain  copy
  1. public class WebView extends AbsoluteLayout  
  2.         implements ViewTreeObserver.OnGlobalFocusChangeListener,  
  3.         ViewGroup.OnHierarchyChangeListener, ViewDebug.HierarchyHandler {  
  4.     ......  
  5.   
  6.     private void ensureProviderCreated() {  
  7.         checkThread();  
  8.         if (mProvider == null) {  
  9.             // As this can get called during the base class constructor chain, pass the minimum  
  10.             // number of dependencies here; the rest are deferred to init().  
  11.             mProvider = getFactory().createWebView(thisnew PrivateAccess());  
  12.         }  
  13.     }  
  14.   
  15.     ......  
  16. }  

       这个函数定义在文件frameworks/base/core/java/android/webkit/WebView.java中。

       WebView类的成员函数ensureProviderCreated首先调用成员函数checkThread确保它是在WebView的创建线程中调用的,接下来又会判断成员变量mProvider的值是否为null。如果为null,就表示它还没有当前创建的WebView创建过Provider。在这种情况下,它首先会调用成员函数getFactory获得一个WebView Factory。有了这个WebView Factory之后,就可以调用它的成员函数createWebView创建一个WebView Provider。

       WebView类的成员函数getFactory的实现如下所示:

[java]   view plain  copy
  1. public class WebView extends AbsoluteLayout  
  2.         implements ViewTreeObserver.OnGlobalFocusChangeListener,  
  3.         ViewGroup.OnHierarchyChangeListener, ViewDebug.HierarchyHandler {  
  4.     ......  
  5.   
  6.     private static synchronized WebViewFactoryProvider getFactory() {  
  7.         return WebViewFactory.getProvider();  
  8.     }  
  9.   
  10.     ......  
  11. }  

       这个函数定义在文件frameworks/base/core/java/android/webkit/WebView.java中。

       WebView类的成员函数getFactory返回的WebView Factory是通过调用WebViewFactory类的静态成员函数getProvider获得的,如下所示:

[java]   view plain  copy
  1. public final class WebViewFactory {  
  2.     ......  
  3.   
  4.     private static WebViewFactoryProvider sProviderInstance;  
  5.     ......  
  6.   
  7.     static WebViewFactoryProvider getProvider() {  
  8.         synchronized (sProviderLock) {  
  9.             // For now the main purpose of this function (and the factory abstraction) is to keep  
  10.             // us honest and minimize usage of WebView internals when binding the proxy.  
  11.             if (sProviderInstance != nullreturn sProviderInstance;  
  12.   
  13.             ......  
  14.             try {  
  15.                 ......  
  16.                 loadNativeLibrary();  
  17.                 ......  
  18.   
  19.                 Class<WebViewFactoryProvider> providerClass;  
  20.                 ......  
  21.                 try {  
  22.                     providerClass = getFactoryClass();  
  23.                 } catch (ClassNotFoundException e) {  
  24.                     ......  
  25.                 } finally {  
  26.                     ......  
  27.                 }  
  28.   
  29.                 ......  
  30.                 try {  
  31.                     sProviderInstance = providerClass.newInstance();  
  32.                     ......  
  33.                     return sProviderInstance;  
  34.                 } catch (Exception e) {  
  35.                     .......  
  36.                 } finally {  
  37.                     ......  
  38.                 }  
  39.             } finally {  
  40.                 ......  
  41.             }  
  42.         }  
  43.     }  
  44.   
  45.     ......  
  46. }  

       这个函数定义在文件frameworks/base/core/java/android/webkit/WebViewFactory.java中。

       WebViewFactory类的静态成员函数getProvider首先是判断静态成员变量sProviderInstance的值是否等于null。如果等于null,那么就说明当前的App进程还没有加载过Chromium动态库。在这种情况下,就需要加载Chromium动态库,并且创建一个WebView Factory,保存在静态成员变量sProviderInstance。接下来我们就先分析Chromium动态库的加载过程,然后再分析WebView Factory的创建过程。

       加载Chromium动态库是通过调用WebViewFactory类的静态成员函数loadNativeLibrary实现的,如下所示: 

[java]   view plain  copy
  1. public final class WebViewFactory {  
  2.     ......  
  3.   
  4.     private static void loadNativeLibrary() {  
  5.         ......  
  6.   
  7.         try {  
  8.             String[] args = getWebViewNativeLibraryPaths();  
  9.             boolean result = nativeLoadWithRelroFile(args[0/* path32 */,  
  10.                                                      args[1/* path64 */,  
  11.                                                      CHROMIUM_WEBVIEW_NATIVE_RELRO_32,  
  12.                                                      CHROMIUM_WEBVIEW_NATIVE_RELRO_64);  
  13.             ......  
  14.         } catch (PackageManager.NameNotFoundException e) {  
  15.             ......  
  16.         }  
  17.     }  
  18.   
  19.     ......  
  20. }  

       这个函数定义在文件frameworks/base/core/java/android/webkit/WebViewFactory.java中。

       WebViewFactory类的静态成员函数loadNativeLibrary首先会调用我们前面分析过的成员函数getWebViewNativeLibraryPaths获得要加载的Chromium动态库的文件路径,然后再调用另外一个静态成员函数nativeLoadWithRelroFile对它进行加载。在加载的时候,会指定一个Chromium GNU_RELRO Section文件。这个Chromium GNU_RELRO Section文件就是前面通过启动一个临时进程生成的。

       WebViewFactory类的静态成员函数nativeLoadWithRelroFile是一个JNI方法,它由C++层的函数LoadWithRelroFile实现,如下所示:

[cpp]   view plain  copy
  1. jboolean LoadWithRelroFile(JNIEnv* env, jclass, jstring lib32, jstring lib64,  
  2.                            jstring relro32, jstring relro64) {  
  3. #ifdef __LP64__  
  4.   jstring lib = lib64;  
  5.   jstring relro = relro64;  
  6.   (void)lib32; (void)relro32;  
  7. #else  
  8.   jstring lib = lib32;  
  9.   jstring relro = relro32;  
  10.   (void)lib64; (void)relro64;  
  11. #endif  
  12.   jboolean ret = JNI_FALSE;  
  13.   const char* lib_utf8 = env->GetStringUTFChars(lib, NULL);  
  14.   if (lib_utf8 != NULL) {  
  15.     const char* relro_utf8 = env->GetStringUTFChars(relro, NULL);  
  16.     if (relro_utf8 != NULL) {  
  17.       ret = DoLoadWithRelroFile(lib_utf8, relro_utf8);  
  18.       env->ReleaseStringUTFChars(relro, relro_utf8);  
  19.     }  
  20.     env->ReleaseStringUTFChars(lib, lib_utf8);  
  21.   }  
  22.   return ret;  
  23. }  

       这个函数定义在文件frameworks/webview/chromium/loader/loader.cpp中。

       函数LoadWithRelroFile判断自己是32位还是64位的实现,然后从参数lib32和lib64中选择对应的Chromium动态库进行加载。这个加载过程是通过调用另外一个函数DoLoadWithRelroFile实现的,如下所示:

[cpp]   view plain  copy
  1. jboolean DoLoadWithRelroFile(const char* lib, const char* relro) {  
  2.   int relro_fd = TEMP_FAILURE_RETRY(open(relro, O_RDONLY));  
  3.   ......  
  4.   
  5.   android_dlextinfo extinfo;  
  6.   extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_USE_RELRO;  
  7.   extinfo.reserved_addr = gReservedAddress;  
  8.   extinfo.reserved_size = gReservedSize;  
  9.   extinfo.relro_fd = relro_fd;  
  10.   void* handle = android_dlopen_ext(lib, RTLD_NOW, &extinfo);  
  11.   close(relro_fd);  
  12.   ......  
  13.   
  14.   return JNI_TRUE;  
  15. }  

       这个函数定义在文件frameworks/webview/chromium/loader/loader.cpp中。

       函数DoLoadWithRelroFile的实现与前面分析的函数DoCreateRelroFile类似,都是通过Linker导出的函数android_dlopen_ext在Zyogote进程保留的地址空间中加载Chromium动态库的。注意,App进程是Zygote进程fork出来的,因此它同样会获得Zygote进程预留的地址空间。

       不过,函数DoLoadWithRelroFile会将告诉函数android_dlopen_ext在加载Chromium动态库的时候,将参数relro描述的Chromium GNU_RELRO Section文件内存映射到内存来,并且代替掉已经加载的Chromium动态库的GNU_RELRO Section。这是通过将指定一个ANDROID_DLEXT_USE_RELRO标志实现的。

       之所以可以这样做,是因为参数relro描述的Chromium GNU_RELRO Section文件对应的Chromium动态库的加载地址与当前App进程加载的Chromium动态库的地址一致。只要两个相同的动态库在两个不同的进程中的加载地址一致,它们的链接和重定位信息就是完全一致的,因此就可以通过文件内存映射的方式进行共享。共享之后,就可以达到节省内存的目的了。

        这一步执行完成之后,App进程就加载完成Chromium动态库了。回到前面分析的WebViewFactory类的静态成员函数getProvider,它接下来继续创建一个WebView Factory。这个WebView Factory以后就可以用来创建WebView Provider。

        WebViewFactory类的静态成员函数getProvider首先要确定要创建的WebView Factory的类型。这个类型是通过调用另外一个静态成员函数getFactoryClass获得的,如下所示:

[java]   view plain  copy
  1. public final class WebViewFactory {  
  2.   
  3.     private static final String CHROMIUM_WEBVIEW_FACTORY =  
  4.             "com.android.webview.chromium.WebViewChromiumFactoryProvider";  
  5.   
  6.     ......  
  7.   
  8.     private static Class<WebViewFactoryProvider> getFactoryClass() throws ClassNotFoundException {  
  9.         Application initialApplication = AppGlobals.getInitialApplication();  
  10.         try {  
  11.             // First fetch the package info so we can log the webview package version.  
  12.             String packageName = getWebViewPackageName();  
  13.             sPackageInfo = initialApplication.getPackageManager().getPackageInfo(packageName, 0);  
  14.             ......  
  15.   
  16.             // Construct a package context to load the Java code into the current app.  
  17.             Context webViewContext = initialApplication.createPackageContext(packageName,  
  18.                     Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);  
  19.             ......  
  20.   
  21.             ClassLoader clazzLoader = webViewContext.getClassLoader();  
  22.             ......  
  23.             try {  
  24.                 return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY, true,  
  25.                                                                      clazzLoader);  
  26.             } finally {  
  27.                 ......  
  28.             }  
  29.         } catch (PackageManager.NameNotFoundException e) {  
  30.             ......  
  31.         }  
  32.     }  
  33.   
  34.     ......  
  35. }  

       这个函数定义在文件frameworks/base/core/java/android/webkit/WebViewFactory.java中。

       从这里可以看到,WebViewFactory类的静态成员函数getFactoryClass返回的WebView Factory的类型为com.android.webview.chromium.WebViewChromiumFactoryProvider。这个com.android.webview.chromium.WebViewChromiumFactoryProvider类是由前面提到的WebView Package提供的。

       这意味着WebViewFactory类的静态成员函数getProvider创建的WebView Factory是一个WebViewChromiumFactoryProvider对象。这个WebViewChromiumFactoryProvider的创建过程如下所示:

[java]   view plain  copy
  1. public class WebViewChromiumFactoryProvider implements WebViewFactoryProvider {  
  2.     ......  
  3.   
  4.     public WebViewChromiumFactoryProvider() {  
  5.         ......  
  6.   
  7.         AwBrowserProcess.loadLibrary();  
  8.   
  9.         .......  
  10.     }  
  11.   
  12.     ......  
  13. }  

       这个函数定义在文件frameworks/webview/chromium/java/com/android/webview/chromium/WebViewChromiumFactoryProvider.java中。

       WebViewChromiumFactoryProvider类的构造函数会调用AwBrowserProcess类的静态成员函数loadLibrary对前面加载的Chromium动态库进行初始化,如下所示:

[java]   view plain  copy
  1. public abstract class AwBrowserProcess {  
  2.     ......  
  3.   
  4.     public static void loadLibrary() {  
  5.         ......  
  6.         try {  
  7.             LibraryLoader.loadNow();  
  8.         } catch (ProcessInitException e) {  
  9.             throw new RuntimeException("Cannot load WebView", e);  
  10.         }  
  11.     }  
  12.   
  13.     ......  
  14. }  

        这个函数定义在文件external/chromium_org/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java中。

        AwBrowserProcess类的静态成员函数loadLibrary又调用LibraryLoader类的静态成员函数loadNow对前面加载的Chromium动态库进行初始化,如下所示:

[java]   view plain  copy
  1. public class LibraryLoader {  
  2.     ......  
  3.   
  4.     public static void loadNow() throws ProcessInitException {  
  5.         loadNow(nullfalse);  
  6.     }  
  7.   
  8.     ......  
  9. }  

        这个函数定义在文件external/chromium_org/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java中。

        LibraryLoader类的静态成员函数loadNow又调用另外一个重载版本的静态成员函数loadNow对前面加载的Chromium动态库进行初始化,如下所示:

[java]   view plain  copy
  1. public class LibraryLoader {  
  2.     ......  
  3.   
  4.     public static void loadNow(Context context, boolean shouldDeleteOldWorkaroundLibraries)  
  5.             throws ProcessInitException {  
  6.         synchronized (sLock) {  
  7.             loadAlreadyLocked(context, shouldDeleteOldWorkaroundLibraries);  
  8.         }  
  9.     }  
  10.   
  11.     ......  
  12. }  

       这个函数定义在文件external/chromium_org/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java中。

       LibraryLoader类重载版本的静态成员函数loadNow又调用另外一个静态成员函数loadAlreadyLocked对前面加载的Chromium动态库进行初始化,如下所示:

[java]   view plain  copy
  1. public class LibraryLoader {  
  2.     ......  
  3.   
  4.     // One-way switch becomes true when the libraries are loaded.  
  5.     private static boolean sLoaded = false;  
  6.     ......  
  7.   
  8.     private static void loadAlreadyLocked(  
  9.             Context context, boolean shouldDeleteOldWorkaroundLibraries)  
  10.             throws ProcessInitException {  
  11.         try {  
  12.             if (!sLoaded) {  
  13.                 ......  
  14.   
  15.                 boolean useChromiumLinker = Linker.isUsed();  
  16.   
  17.                 if (useChromiumLinker) Linker.prepareLibraryLoad();  
  18.   
  19.                 for (String library : NativeLibraries.LIBRARIES) {  
  20.                     Log.i(TAG, "Loading: " + library);  
  21.                     if (useChromiumLinker) {  
  22.                         Linker.loadLibrary(library);  
  23.                     } else {  
  24.                         try {  
  25.                             System.loadLibrary(library);  
  26.                         } catch (UnsatisfiedLinkError e) {  
  27.                             ......  
  28.                         }  
  29.                     }  
  30.                 }  
  31.                 if (useChromiumLinker) Linker.finishLibraryLoad();  
  32.   
  33.                 ......  
  34.                 sLoaded = true;  
  35.             }  
  36.         } catch (UnsatisfiedLinkError e) {  
  37.             ......  
  38.         }  
  39.         ......  
  40.     }  
  41.   
  42.     ......  
  43. }  

       这个函数定义在文件external/chromium_org/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java中。

       由于并不是所有的系统都支持在加载动态库时,以文件内存映射的方式代替它的GNU_RELRO Section,因此Chromium自己提供了一个Linker。通过这个Linker加载动态库时,能够以文件内存映射的方式代替要加载的动态库的GNU_RELRO Section,也就是实现前面提到的函数android_dlopen_ext的功能。     

       在Android 5.0中,由于系统已经提供了函数android_dlopen_ext,因此,Chromium就不会使用自己的Linker加载动态库,而是使用Android系统提供的Linker来加载动态库。通过调用System类的静态成员函数loadLibrary即可以使用系统提供的Linker来加载动态库。

       LibraryLoader类的静态成员函数loadAlreadyLocked要加载的动态库由NativeLibraries类的静态成员变量LIBRARIES指定,如下所示:

[java]   view plain  copy
  1. public class NativeLibraries {  
  2.     ......  
  3.   
  4.     static final String[] LIBRARIES = { "webviewchromium" };  
  5.       
  6.     ......  
  7. }  

      这个静态成员变量定义在文件external/chromium_org/android_webview/java/generated_src/org/chromium/base/library_loader/NativeLibraries.java中。

      从这里可以知道,LibraryLoader类的静态成员函数loadAlreadyLocked要加载的动态库就是Chromium动态库。这个Chromium动态库前面已经加载过了,因此这里通过调用System类的静态成员函数loadLibrary再加载时,仅仅是只会触发它导出的函数JNI_OnLoad被调用,而不会重新被加载。

      Chromium动态库导出的JNI_OnLoad被调用的时候,Chromium动态库就会执行初始化工作,如下所示:

[cpp]   view plain  copy
  1. JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {  
  2.   ......  
  3.   
  4.   content::SetContentMainDelegate(new android_webview::AwMainDelegate());  
  5.   ......  
  6.   
  7.   return JNI_VERSION_1_4;  
  8. }  

       这个函数定义在文件external/chromium_org/android_webview/lib/main/webview_entry_point.cc中。

       其中的一个初始化操作是给Chromium的Content层设置一个类型为AwMainDelegate的Main Delegate。这个AwMainDelegate实现在Chromium的android_webview模块中。Android WebView是通过Chromium的android_webview模块加载和渲染网页的。Chromium加载和渲染网页的功能又是实现在Content层的,因此,Chromium的android_webview模块又要通过Content层实现加载和渲染网页功能。这样,Chromium的android_webview模块就可以设置一个Main Delegate给Content层,以便它们可以互相通信。

       给Chromium的Content层设置一个Main Delegate是通过调用函数SetContentMainDelegate实现的,它的实现如下所示:

[cpp]   view plain  copy
  1. LazyInstance<scoped_ptr<ContentMainDelegate> > g_content_main_delegate =  
  2.     LAZY_INSTANCE_INITIALIZER;  
  3.   
  4. ......  
  5.   
  6. void SetContentMainDelegate(ContentMainDelegate* delegate) {  
  7.   DCHECK(!g_content_main_delegate.Get().get());  
  8.   g_content_main_delegate.Get().reset(delegate);  
  9. }  

       这个函数定义在文件external/chromium_org/content/app/android/content_main.cc中。

       从前面的分析可以知道,参数delegate指向的是一个AwMainDelegate对象,这个AwMainDelegate对象会被函数SetContentMainDelegate保存在全局变量g_content_main_delegate中。在接下来一篇文章中分析Chromium渲染引擎的启动过程时,我们就会看到这个AwMainDelegate对象的作用。

       这一步执行完成后,Chromium动态库就在App进程中加载完毕,并且也已经完成了初始化工作。与此同时,系统也为App进程创建了一个类型为WebViewChromiumFactoryProvider的WebView Factory。回到前面分析的WebView类的成员函数ensureProviderCreated中,这时候就它会通过调用上述类型为WebViewChromiumFactoryProvider的WebView Factory的成员函数createWebView为当前创建的WebView创建一个WebView Provider,如下所示:

[cpp]   view plain  copy
  1. public class WebViewChromiumFactoryProvider implements WebViewFactoryProvider {  
  2.     ......  
  3.   
  4.     @Override  
  5.     public WebViewProvider createWebView(WebView webView, WebView.PrivateAccess privateAccess) {  
  6.         WebViewChromium wvc = new WebViewChromium(this, webView, privateAccess);  
  7.   
  8.         ......  
  9.   
  10.         return wvc;  
  11.     }  
  12.   
  13.     ......  
  14. }  

       这个函数定义在文件frameworks/webview/chromium/java/com/android/webview/chromium/WebViewChromiumFactoryProvider.java中。

       WebViewChromiumFactoryProvider类的成员函数createWebView创建的是一个类型为WebViewChromium的WebView Provider。这个WebView Provider将会返回给WebView类的成员函数ensureProviderCreated。WebView类的成员函数ensureProviderCreated再将该WebView Provider保存在成员变量mProvider中。

       这样,正在创建的WebView就获得了一个类型为WebViewChromium的WebView Provider。以后通过这个WebView Provider,就可以通过Chromium来加载和渲染网页了。

       至此,我们也分析完成了Android WebView加载Chromium动态库的过程。在接下来的一篇文章中,我们继续分析Android WebView启动Chromium渲染引擎的过程。Chromium渲染引擎启动起来之后,Android WebView就可以通过它来加载和渲染网页了。敬请关注!更多的信息也可以关注老罗的新浪微博:http://weibo.com/shengyangluo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值