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:

  1. The thread check
  2. Provider class selection
  3. Initialization of the Provider class
  4. 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

  1. Determine the PackageInfo and Context of the WebView
  2. 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…