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

上传人:b****1 文档编号:23198993 上传时间:2023-05-15 格式:DOCX 页数:43 大小:133.83KB
下载 相关 举报
Android WebView加载Chromium动态库的过程分析.docx_第1页
第1页 / 共43页
Android WebView加载Chromium动态库的过程分析.docx_第2页
第2页 / 共43页
Android WebView加载Chromium动态库的过程分析.docx_第3页
第3页 / 共43页
Android WebView加载Chromium动态库的过程分析.docx_第4页
第4页 / 共43页
Android WebView加载Chromium动态库的过程分析.docx_第5页
第5页 / 共43页
点击查看更多>>
下载资源
资源描述

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

《Android WebView加载Chromium动态库的过程分析.docx》由会员分享,可在线阅读,更多相关《Android WebView加载Chromium动态库的过程分析.docx(43页珍藏版)》请在冰豆网上搜索。

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

AndroidWebView加载Chromium动态库的过程分析

AndroidWebView加载Chromium动态库的过程分析

Chromium动态库的体积比较大,有27M左右,其中程序段和数据段分别占据25.65M和1.35M。

如果按照通常方式加载Chromium动态库,那么当有N个正在运行的App使用WebView时,系统需要为Chromium动态库分配的内存为(25.65+Nx1.35)M。

这是非常可观的。

为此,Android使用了特殊的方式加载Chromium动态库。

本文接下来就详细分析这种特殊的加载方式。

为什么当有N个正在运行的App使用WebView时,系统需要为Chromium动态库分配的内存为(25.65+Nx1.35)M呢?

这是由于动态库的程序段是只读的,可以在多个进程之间进行共享,但是数据段一般是可读可写的,不能共享。

在1.35M的数据段中,有1.28M在Chromium动态库加载完成后就是只读的。

这1.28M数据包含有C++虚函数表,以及指针类型的常量等,它们在编译的时候会放在一个称为GNU_RELRO的Section中,如图1所示:

如果我们将该GNU_RELROSection看作是一般的数据段,那么系统就需要为每一个使用了WebView的App进程都分配一段1.28M大小的内存空间。

前面说到,这1.28M数据在Chromium动态库加载完成后就是只读的,那么有没有办法让它像程序段一样,在多个App进程之间共享呢?

只要能满足一个条件,那么答案就是肯定的。

这个条件就是所有的App进程都在相同的虚拟地址空间加载Chromium动态库。

在这种情况下,就可以在系统启动的过程中,创建一个临时进程,并且在这个进程中加载Chromium动态库。

假设Chromium动态库的加载地址为BaseAddress。

加载完成后,将Chromium动态库的GNU_RELROSection的内容Dump出来,并且写入到一个文件中去。

以后App进程加载Chromium动态库时,都将Chromium动态库加载地址BaseAddress上,并且使用内存映射的方式将前面Dump出来的GNU_RELRO文件代替Chromium动态库的GNU_RELROSection。

这样就可以实现在多个App进程之间共享Chromium动态库的GNU_RELROSection了,如图2所示:

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

还有两个问题需要解决。

第一个问题是要将加载后的Chromium动态库的GNU_RELROSection的内容Dump出来,并且写入到一个文件中去。

第二个问题是要让所有的App进程将Chromium动态加载在指定位置,并且可以使用指定的文件来代替它的GNU_RELROSection。

这两个问题都可以通过Android在5.0版本提供的一个动态库加载函数android_dlopen_ext解决。

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

从前面一文可以知道,Zygote进程在Java层的入口函数为ZygoteInit类的静态成员函数main,它的实现如下所示:

[java]viewplaincopy

publicclassZygoteInit{

......

publicstaticvoidmain(Stringargv[]){

try{

.......

preload();

.......

if(startSystemServer){

startSystemServer(abiList,socketName);

}

......

runSelectLoop(abiList);

......

}catch(MethodAndArgsCallercaller){

......

}catch(RuntimeExceptionex){

......

}

}

......

}

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

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

这些预加载的资源以后就可以在App进程之间进行共享。

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

[java]viewplaincopy

publicclassZygoteInit{

......

staticvoidpreload(){

......

//AsktheWebViewFactorytodoanyinitializationthatmustruninthezygoteprocess,

//formemorysharingpurposes.

WebViewFactory.prepareWebViewInZygote();

......

}

......

}

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

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

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

publicfinalclassWebViewFactory{

......

publicstaticvoidprepareWebViewInZygote(){

try{

System.loadLibrary("webviewchromium_loader");

longaddressSpaceToReserve=

SystemProperties.getLong(CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY,

CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES);

sAddressSpaceReserved=nativeReserveAddressSpace(addressSpaceToReserve);

......

}catch(Throwablet){

......

}

}

......

}

这个函数定义在文件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]viewplaincopy在CODE上查看代码片派生到我的代码片

jbooleanReserveAddressSpace(JNIEnv*,jclass,jlongsize){

returnDoReserveAddressSpace(size);

}

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

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

[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

void*gReservedAddress=NULL;

size_tgReservedSize=0;

jbooleanDoReserveAddressSpace(jlongsize){

size_tvsize=static_cast(size);

void*addr=mmap(NULL,vsize,PROT_NONE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0);

......

gReservedAddress=addr;

gReservedSize=vsize;

......

returnJNI_TRUE;

}

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

函数DoReserveAddressSpace是通过系统接口mmap预留指定大小的地址空间的。

同时,预留出来的地址空间的起始地址和大小分别记录在全局变量gReservedAddress和gReservedSize中。

以后Chromium动态库就可以加载在该地址空间中。

为Chromium动态库预留好加载地址之后,Android系统接下来要做的一件事情就是请求Zygote进程启动一个临时进程。

这个临时进程将会在上述预留的地址空间中加载Chromium动态库。

这个Chromium动态库在加载的过程中,它的GNU_RELROSection会被重定位。

重定位完成之后,就可以将它的内容Dump到一个文件中去。

这个文件以后就会以内存映射的方式代替在App进程中加载的Chromium动态库的GNU_RELROSection。

请求Zygote进程启动临时进程的操作是由System进程完成的。

从前面一文可以知道,System进程是由Zygote进程启动的,它在Java层的入口函数为SystemServer的静态成员函数main,它的实现如下所示:

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

publicfinalclassSystemServer{

......

publicstaticvoidmain(String[]args){

newSystemServer().run();

}

......

}

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

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

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

publicfinalclassSystemServer{

......

privatevoidrun(){

......

//Startservices.

try{

startBootstrapServices();

startCoreServices();

startOtherServices();

}catch(Throwableex){

......

}

......

//Loopforever.

Looper.loop();

thrownewRuntimeException("Mainthreadloopunexpectedlyexited");

}

......

}

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

SystemServer类的成员函数run首先会调用成员函数startBootstrapServices启动Bootstrap类型的系统服务。

这些系统服务包括ActivityManagerService、PowerManagerService、PackageManagerService和DisplayManagerService等。

SystemServer类的成员函数run接下来又会调用成员函数startCoreServices启动Core类型的系统服务。

这些系统服务包括LightsService、BatteryService和UsageStatsService等。

SystemServer类的成员函数run再接下来还会调用成员函数startOtherServices启动其它的系统服务。

这些其它的系统服务包括AccountManagerService、NetworkManagementService和WindowManagerService等。

SystemServer类的成员函数startOtherServices启动完成其它的系统服务之后,就会创建一个Runnable。

这个Runnable会在ActivityMangerService启动完成后执行,如下所示:

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

publicfinalclassSystemServer{

......

privatevoidstartOtherServices(){

finalContextcontext=mSystemContext;

AccountManagerServiceaccountManager=null;

ContentServicecontentService=null;

VibratorServicevibrator=null;

IAlarmManageralarm=null;

MountServicemountService=null;

NetworkManagementServicenetworkManagement=null;

NetworkStatsServicenetworkStats=null;

NetworkPolicyManagerServicenetworkPolicy=null;

ConnectivityServiceconnectivity=null;

NetworkScoreServicenetworkScore=null;

NsdServiceserviceDiscovery=null;

WindowManagerServicewm=null;

BluetoothManagerServicebluetooth=null;

UsbServiceusb=null;

SerialServiceserial=null;

NetworkTimeUpdateServicenetworkTimeUpdater=null;

CommonTimeManagementServicecommonTimeMgmtService=null;

InputManagerServiceinputManager=null;

TelephonyRegistrytelephonyRegistry=null;

ConsumerIrServiceconsumerIr=null;

AudioServiceaudioService=null;

MmsServiceBrokermmsService=null;

......

mActivityManagerService.systemReady(newRunnable(){

@Override

publicvoidrun(){

......

WebViewFactory.prepareWebViewInSystemServer();

......

}

});

}

......

}

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

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

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

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

publicfinalclassWebViewFactory{

......

publicstaticvoidprepareWebViewInSystemServer(){

String[]nativePaths=null;

try{

nativePaths=getWebViewNativeLibraryPaths();

}catch(Throwablet){

//Loganddiscarderrorsatthisstageaswemustnotcrashthesystemserver.

Log.e(LOGTAG,"errorpreparingwebviewnativelibrary",t);

}

prepareWebViewInSystemServer(nativePaths);

}

......

}

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

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

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

publicfinalclassWebViewFactory{

......

privatestaticString[]getWebViewNativeLibraryPaths()

throwsPackageManager.NameNotFoundException{

finalStringNATIVE_LIB_FILE_NAME="libwebviewchromium.so";

PackageManagerpm=AppGlobals.getInitialApplication().getPackageManager();

ApplicationInfoai=pm.getApplicationInfo(getWebViewPackageName(),0);

Stringpath32;

Stringpath64;

booleanprimaryArchIs64bit=VMRuntime.is64BitAbi(ai.primaryCpuAbi);

if(!

TextUtils.isEmpty(ai.secondaryCpuAbi)){

//Multi-archcase.

if(primaryArchIs64bit){

//Primaryarch:

64-bit,secondary:

32-bit.

path64=ai.nativeLibraryDir;

path32=ai.secondaryNativeLibraryDir;

}else{

//Primaryarch:

32-bit,secondary:

64-bit.

path64=ai.secondaryNativeLibraryDir;

path32=ai.nativeLibraryDir;

}

}elseif(primaryArchIs64bit){

//Single-arch64-bit.

path64=ai.nativeLibraryDir;

path32="";

}else{

//Single-arch32-bit.

path32=ai.nativeLibraryDir;

path64="";

}

if(!

TextUtils.isEmpty(path32))path32+="/"+NATIVE_LIB_FILE_NAME;

if(!

TextUtils.isEmpty(path64))path64+="/"+NATIVE_LIB_FILE_NAME;

returnnewString[]{path32,path64};

}

......

}

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

Android系统将Chromium动态库打包在一个We

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 幼儿教育 > 育儿知识

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1