The webview initialization
Watch how Android loads the WebView kernel. Let’s start with the init process of the WebView. The constructor of the WebView will eventually be called
WebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes,
Map<String, Object> javaScriptInterfaces, boolean privateBrowsing)
Copy the code
This method, this method does several things:
- The thread check
- Provider class selection
- Initialization of the Provider class
- Cookie-related processing
Thread checking is to ensure that webViews are created in the same thread. Focus the main experience on provider-related logic.
MProvider is a WebView member variable of type WebViewProvider. In fact, we can see that all the logic of a WebView is handled by a WebViewProvider, for example:
The source implementation of the load(String URL) method is
public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
checkThread();
mProvider.loadUrl(url, additionalHttpHeaders);
}
Copy the code
WebViewProvider is actually an interface into the source code, which includes most of the same name methods in WebView. Including loadUrl,reload,goBack, etc. So he’s a real power provider for WebView.
Look at the source code ensureProviderCreated() method
mProvider = getFactory().createWebView(this.new PrivateAccess());
Copy the code
The concrete implementation of getFactory is
private static synchronized WebViewFactoryProvider getFactory(a) {
return WebViewFactory.getProvider();
}
Copy the code
This step is to determine the subclass of WebViewProvider, which is constructed according to the WebViewFactory class. Android switched the WebView kernel from WebKit to Chromium after 5.0, where factory mode is used to decouple the kernel implementation reflection from the upper-level initialization code
We select the core logic in the getProvider() method for analysis
try {
Class<WebViewFactoryProvider> providerClass = getProviderClass();
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "providerClass.newInstance()");
try {
sProviderInstance = providerClass.getConstructor(WebViewDelegate.class)
.newInstance(new WebViewDelegate());
return sProviderInstance;
} catch (Exception e) {
} finally{}finally{}}Copy the code
Some versions are implemented as
try {
Class<WebViewFactoryProvider> providerClass = getProviderClass();
Method staticFactory = null;
try {
staticFactory = providerClass.getMethod(CHROMIUM_WEBVIEW_FACTORY_METHOD, WebViewDelegate.class);
} catch (Exception e) {
}
try {
sProviderInstance = (WebViewFactoryProvider)staticFactory.invoke(null.new WebViewDelegate());
return sProviderInstance;
} catch (Exception e) {
}
} catch (Exception e) {
}
Copy the code
Here, after obtaining the actual FactoryProvider class, reflection is used to create the real FactoryProvider object
So how exactly do you know you should get that FactoryProvider object? Look at the code for the getProviderClass() method:
Context webViewContext = null;
Application initialApplication = AppGlobals.getInitialApplication();
try {
try {
webViewContext = getWebViewContextAndSetProvider();
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
try {
ClassLoader clazzLoader = webViewContext.getClassLoader();
loadNativeLibrary(clazzLoader);
try {
return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY,true, clazzLoader);
} finally{}}catch() {}}catch (MissingWebViewPackageException e) {
try {
return (Class<WebViewFactoryProvider>) Class.forName(NULL_WEBVIEW_FACTORY);
} catch (ClassNotFoundException e2) {
// Ignore.}}Copy the code
You can see that there are two types of Provider results
- CHROMIUM_WEBVIEW_FACTORY com.android.webview.chromium.WebViewChromiumFactoryProvider
- NULL_WEBVIEW_FACTORY com.android.webview.nullwebview.NullWebViewFactoryProvider
Normal loading results can be roughly divided into two steps
- Determine the PackageInfo and Context of the WebView
- Load library based on this result
The analysis of the first step, we check method getWebViewContextAndSetProvider ()
The core logic inside is
WebViewProviderResponse response = null;
response = getUpdateService().waitForAndGetProvider();
PackageInfo newPackageInfo = null;
newPackageInfo = initialApplication.getPackageManager().getPackageInfo(
response.packageInfo.packageName,
PackageManager.GET_SHARED_LIBRARY_FILES
| PackageManager.MATCH_DEBUG_TRIAGED_MISSING
// Make sure that we fetch the current provider even if its not
// installed for the current user
| PackageManager.MATCH_UNINSTALLED_PACKAGES
// Fetch signatures for verification
| PackageManager.GET_SIGNATURES
// Get meta-data for meta data flag verification
| PackageManager.GET_META_DATA);
verifyPackageInfo(response.packageInfo, newPackageInfo);
Context webViewContext = initialApplication.createApplicationContext(
newPackageInfo.applicationInfo,
Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
sPackageInfo = newPackageInfo;
return webViewContext;
Copy the code
We’ll focus on tracing the waitForAndGetProvider() method. This method calls the waitForAndGetProvider() method of the WebViewUpdateServiceImpl class, which then calls the waitForAndGetProvider() method of the WebViewUpdate class.
The WebViewProviderResponse object is created in waitForAndGetProvider() based on mCurrentWebViewPackage
In onWebViewProviderChanged(), we get the Package object. In findPreferredWebViewPackage () method, we can get the latest package
FindPreferredWebViewPackage () for the core logic
String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(mContext);
Copy the code
If the user has already selected the WebView, use the user-selected one
for (ProviderAndPackageInfo providerAndPackage : providers) {
if (providerAndPackage.provider.packageName.equals(userChosenProvider)) {
// userPackages can contain null objects.
List<UserPackage> userPackages =
mSystemInterface.getPackageInfoForProviderAllUsers(mContext,providerAndPackage.provider);
if (isInstalledAndEnabledForAllUsers(userPackages)) {
returnproviderAndPackage.packageInfo; }}}Copy the code
If the selection fails or there is no selection, use the most stable one
for (ProviderAndPackageInfo providerAndPackage : providers) {
if (providerAndPackage.provider.availableByDefault) {
// userPackages can contain null objects.
List<UserPackage> userPackages =
mSystemInterface.getPackageInfoForProviderAllUsers(mContext,providerAndPackage.provider);
if (isInstalledAndEnabledForAllUsers(userPackages)) {
returnproviderAndPackage.packageInfo; }}}Copy the code
So how does Android get all the packages? In com. Android. Server. Its. SystemImpl in have the answer,
parser = AppGlobals.getInitialApplication().getResources().getXml(
com.android.internal.R.xml.config_webview_packages);
Copy the code
The following logic is invoked in SystemImpl. It turns out that webView-related package information is stored in an XML file.
Config_webview_packes.xml has the following contents:
<webviewproviders>
<! -- The default WebView implementation -->
<webviewprovider description="Android WebView" packageName="com.android.webview" />
</webviewproviders>
Copy the code
The default Package name of system WebView on Android is com.android.webview. After Android 7.0, users can select webView implementation in Settings. If the user has the Chrome Android App installed on their phone, they can choose the WebView implementation as Chrome
So here we are tracing the Context and Provider that we used to get the WebView in the first step. It’s going to return the Context.
Next, analyze the second step, which is to actually load the Chromium dynamic library of webView
The core logic of the loadNativeLibrary method is as follows
String[] args = getWebViewNativeLibraryPaths(sPackageInfo);
int result = nativeLoadWithRelroFile(args[0] /* path32 */,
args[1] /* path64 */,
CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
CHROMIUM_WEBVIEW_NATIVE_RELRO_64,
clazzLoader);
return result;
Copy the code
Go getWebViewNativeLibraryPaths, this is to obtain the webview dynamic library path, respectively, also of 32-bit and 64 – bit systems. And this dynamic library is in the form of an APK file. The following call is made in the getLoadFromApkPath method
try (ZipFile z = new ZipFile(apkPath)) {
for (String abi : abiList) {
final String entry = "lib/" + abi + "/" + nativeLibFileName;
ZipEntry e = z.getEntry(entry);
if(e ! =null && e.getMethod() == ZipEntry.STORED) {
// Return a path formatted for dlopen() load from APK.
return apkPath + ! "" /"+ entry; }}}catch (IOException e) {
throw new MissingWebViewPackageException(e);
}
Copy the code
The loadWithRelroFile method calls a JNI method nativeLoadWithRelroFile()
We can find the source code framework/base/native/webview find loader. The CPP has jint LoadWithRelroFile (env JNIEnv *, jclass, jstring lib. Jstring relro32, jstring relro64, jobject clazzLoader This method calls the jint DoLoadWithRelroFile(JNIEnv* env, const char* lib, const char* relro, jobject clazzLoader) method, which has the following internal logic
int relro_fd = TEMP_FAILURE_RETRY(open(relro, O_RDONLY));
android_namespace_t* ns =
android::FindNamespaceByClassLoader(env, clazzLoader);
android_dlextinfo extinfo;
extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_USE_RELRO |
ANDROID_DLEXT_USE_NAMESPACE;
extinfo.reserved_addr = gReservedAddress;
extinfo.reserved_size = gReservedSize;
extinfo.relro_fd = relro_fd;
extinfo.library_namespace = ns;
void* handle = android_dlopen_ext(lib, RTLD_NOW, &extinfo);
close(relro_fd);
return LIBLOAD_SUCCESS;
Copy the code
The function DoLoadWithRelroFile will tell android_dlopen_ext when loading the Chromium dynamic library, Map the Chromium GNU_RELRO Section file memory described by relro to memory and replace the GNU_RELRO Section of the loaded Chromium dynamic library. This is done by specifying an ANDROID_DLEXT_USE_RELRO flag.
This can be done because the loading address of the Chromium dynamic library corresponding to the Chromium GNU_RELRO Section file described by relro is the same as that of the Chromium dynamic library loaded by the current App process. As long as the loading addresses of two identical dynamic libraries are the same in two different processes, their linking and relocation information is exactly the same and can therefore be shared through file memory mapping. Once shared, you can save memory.”
This completes the loading of the Chromium dynamic library.
Back to the webview initialization, will continue to call WebViewFactoryProvider createWebView method, if the load of chromium, so the interface implementation class is WebViewChromiumFactoryProvider, It returns a WebViewChromium object in createWebView
WebViewChromium wvc = new WebViewChromium(this, webView, privateAccess);
synchronized (mLock) {
if(mWebViewsToStart ! =null) {
mWebViewsToStart.add(newWeakReference<WebViewChromium>(wvc)); }}return wvc;
Copy the code
Our actual Provider class is WebViewChromium, and then we call the Init method of WebViewChromium, which initializes the real WebView engine, including the loading of modules such as rendering
So much for WebView initialization. We will continue to analyze the workflow of the Provider and how WebView loads the dynamic library
Refer to the article address: blog.csdn.net/luoshengyan…