Android 7.1 WebView 实现方式选择

Chrome方法已废弃

后续测试, Chrome已经无法使用此方法, 也找不到当初对应测试的版本
Google Chrome app is no longer the WebView provider in Android 10

后续采用Google WebView, 改法参照下文

<webviewproviders>
    <!-- The default WebView implementation -->
    <webviewprovider description="Android WebView" packageName="com.android.webview" availableByDefault="true">
    </webviewprovider>
    <webviewprovider description="Google WebView" packageName="com.google.android.webview" availableByDefault="false">
    </webviewprovider>
</webviewproviders>

平台

RK3288 + Android 7.1

说明

选项位置: 设置 > 开发者选项 > WebView实现

1.在Pixel上, 可以显示两个选项: Android System WebView 和 Chrome
尝试把Chorme 禁用后, 选项中, 仅剩下Android System WebView.

2.尝试在RK3288主板上安装Chrome, 并增加相应配置

	<webviewprovider description="Chrome WebView X" packageName="com.android.chrome" availableByDefault="true">
    </webviewprovider>

编译烧录后, 可以看到, 新增了Chrome WebView X, 测试选择不同的实现方法, 均可正常使用.

为确认装入Chrome后可以生效, 已删除源码自带/system/app/webview

相关源码

|-- packages/apps/Settings/src/com/android/settings/DevelopmentSettings.java

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);

        mWindowManager = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
        mBackupManager = IBackupManager.Stub.asInterface(
                ServiceManager.getService(Context.BACKUP_SERVICE));
//获取 IWebViewUpdateService
        mWebViewUpdateService  =
            IWebViewUpdateService.Stub.asInterface(ServiceManager.getService("webviewupdate"));
			...
	}

	
    private void updateWebViewProviderOptions() {
        try {
// 从IWebViewUpdateService中获取可用的WebView 包名, 并填充列表.
            WebViewProviderInfo[] providers = mWebViewUpdateService.getValidWebViewPackages();
            if (providers == null) {
                Log.e(TAG, "No WebView providers available");
                return;
            }
            ArrayList<String> options = new ArrayList<String>();
            ArrayList<String> values = new ArrayList<String>();
            for(int n = 0; n < providers.length; n++) {
                if (Utils.isPackageEnabled(getActivity(), providers[n].packageName)) {
                    options.add(providers[n].description);
                    values.add(providers[n].packageName);
                }
            }
            mWebViewProvider.setEntries(options.toArray(new String[options.size()]));
            mWebViewProvider.setEntryValues(values.toArray(new String[values.size()]));

            String value = mWebViewUpdateService.getCurrentWebViewPackageName();
            if (value == null) {
                value = "";
            }

            for (int i = 0; i < values.size(); i++) {
                if (value.contentEquals(values.get(i))) {
                    mWebViewProvider.setValueIndex(i);
                    return;
                }
            }
        } catch(RemoteException e) {
        }
    }

|-- frameworks/base/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java

	public WebViewUpdateServiceImpl(Context context, SystemInterface systemInterface) {
        mContext = context;
        mSystemInterface = systemInterface;
        mWebViewUpdater = new WebViewUpdater(mContext, mSystemInterface);
    }

    WebViewProviderInfo[] getValidWebViewPackages() {
        return mWebViewUpdater.getValidAndInstalledWebViewPackages();
    }
    private static class WebViewUpdater {
        private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos(boolean onlyInstalled) {
            WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
            List<ProviderAndPackageInfo> providers = new ArrayList<>();
            for(int n = 0; n < allProviders.length; n++) {
                try {
                    PackageInfo packageInfo =
                        mSystemInterface.getPackageInfoForProvider(allProviders[n]);
                    if ((!onlyInstalled || isInstalledPackage(packageInfo))
                            && isValidProvider(allProviders[n], packageInfo)) {
                        providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo));
                    }
                } catch (NameNotFoundException e) {
                    // Don't add non-existent packages
                }
            }
            return providers.toArray(new ProviderAndPackageInfo[providers.size()]);
        }
	}

|-- frameworks/base/services/core/java/com/android/server/webkit/WebViewUpdateService.java

    public WebViewUpdateService(Context context) {
        super(context);
        mImpl = new WebViewUpdateServiceImpl(context, SystemImpl.getInstance());
    }

|-- frameworks/base/services/core/java/com/android/server/webkit/SystemInterface.java

public class SystemImpl implements SystemInterface {
	...
    private final WebViewProviderInfo[] mWebViewProviderPackages;

    // Initialization-on-demand holder idiom for getting the WebView provider packages once and
    // for all in a thread-safe manner.
    private static class LazyHolder {
        private static final SystemImpl INSTANCE = new SystemImpl();
    }

    public static SystemImpl getInstance() {
        return LazyHolder.INSTANCE;
    }

    private SystemImpl() {
        int numFallbackPackages = 0;
        int numAvailableByDefaultPackages = 0;
        int numAvByDefaultAndNotFallback = 0;
        XmlResourceParser parser = null;
        List<WebViewProviderInfo> webViewProviders = new ArrayList<WebViewProviderInfo>();
        try {
//读取系统配置文件config_webview_packages.xml中的值:
            parser = AppGlobals.getInitialApplication().getResources().getXml(
                    com.android.internal.R.xml.config_webview_packages);
            XmlUtils.beginDocument(parser, TAG_START);
            while(true) {
                XmlUtils.nextElement(parser);
                String element = parser.getName();
                if (element == null) {
                    break;
                }
                if (element.equals(TAG_WEBVIEW_PROVIDER)) {
                    String packageName = parser.getAttributeValue(null, TAG_PACKAGE_NAME);
                    if (packageName == null) {
                        throw new AndroidRuntimeException(
                                "WebView provider in framework resources missing package name");
                    }
                    String description = parser.getAttributeValue(null, TAG_DESCRIPTION);
                    if (description == null) {
                        throw new AndroidRuntimeException(
                                "WebView provider in framework resources missing description");
                    }
                    boolean availableByDefault = "true".equals(
                            parser.getAttributeValue(null, TAG_AVAILABILITY));
                    boolean isFallback = "true".equals(
                            parser.getAttributeValue(null, TAG_FALLBACK));
                    WebViewProviderInfo currentProvider = new WebViewProviderInfo(
                            packageName, description, availableByDefault, isFallback,
                            readSignatures(parser));
                    if (currentProvider.isFallback) {
                        numFallbackPackages++;
                        if (!currentProvider.availableByDefault) {
                            throw new AndroidRuntimeException(
                                    "Each WebView fallback package must be available by default.");
                        }
                        if (numFallbackPackages > 1) {
                            throw new AndroidRuntimeException(
                                    "There can be at most one WebView fallback package.");
                        }
                    }
                    if (currentProvider.availableByDefault) {
                        numAvailableByDefaultPackages++;
                        if (!currentProvider.isFallback) {
                            numAvByDefaultAndNotFallback++;
                        }
                    }
                    webViewProviders.add(currentProvider);
                }
                else {
                    Log.e(TAG, "Found an element that is not a WebView provider");
                }
            }
        } catch (XmlPullParserException | IOException e) {
            throw new AndroidRuntimeException("Error when parsing WebView config " + e);
        } finally {
            if (parser != null) parser.close();
        }
        if (numAvailableByDefaultPackages == 0) {
            throw new AndroidRuntimeException("There must be at least one WebView package "
                    + "that is available by default");
        }
        if (numAvByDefaultAndNotFallback == 0) {
            throw new AndroidRuntimeException("There must be at least one WebView package "
                    + "that is available by default and not a fallback");
        }
        mWebViewProviderPackages =
                webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]);
    }

|-- frameworks/base/core/res/res/xml/config_webview_packages.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2015 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->

<webviewproviders>
    <!-- The default WebView implementation -->
    <webviewprovider description="Android WebView" packageName="com.android.webview" availableByDefault="true">
    </webviewprovider>
</webviewproviders>

扩展

|-- frameworks/base/core/java/android/webkit/WebView.java

    private WebViewProvider mProvider;
    protected WebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes,
            Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
        super(context, attrs, defStyleAttr, defStyleRes);
        if (context == null) {
            throw new IllegalArgumentException("Invalid context argument");
        }
        sEnforceThreadChecking = context.getApplicationInfo().targetSdkVersion >=
                Build.VERSION_CODES.JELLY_BEAN_MR2;
        checkThread();

        ensureProviderCreated();
        mProvider.init(javaScriptInterfaces, privateBrowsing);
        // Post condition of creating a webview is the CookieSyncManager.getInstance() is allowed.
        CookieSyncManager.setGetInstanceIsAllowed();
    }

    private void ensureProviderCreated() {
        checkThread();
        if (mProvider == null) {
            // As this can get called during the base class constructor chain, pass the minimum
            // number of dependencies here; the rest are deferred to init().
            mProvider = getFactory().createWebView(this, new PrivateAccess());
        }
    }

	private static synchronized WebViewFactoryProvider getFactory() {
        return WebViewFactory.getProvider();
    }
//加载网址
    public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
        checkThread();
        mProvider.loadUrl(url, additionalHttpHeaders);
    }

|-- frameworks/base/core/java/android/webkit/WebViewFactory.java

    static WebViewFactoryProvider getProvider() {
        synchronized (sProviderLock) {
			...
            try {
                Class<WebViewFactoryProvider> providerClass = getProviderClass();
                try {
                    sProviderInstance = providerClass.getConstructor(WebViewDelegate.class)
                            .newInstance(new WebViewDelegate());
                    if (DEBUG) Log.v(LOGTAG, "Loaded provider: " + sProviderInstance);
                    return sProviderInstance;
                } catch (Exception e) {
					...
                }
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
                StrictMode.setThreadPolicy(oldPolicy);
            }
        }
    }

	private static Class<WebViewFactoryProvider> getProviderClass() {
        Context webViewContext = null;
        Application initialApplication = AppGlobals.getInitialApplication();

        try {
            
            try {
                webViewContext = getWebViewContextAndSetProvider();
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
            }
            Log.i(LOGTAG, "Loading " + sPackageInfo.packageName + " version " +
                    sPackageInfo.versionName + " (code " + sPackageInfo.versionCode + ")");

            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()");
            try {
                initialApplication.getAssets().addAssetPathAsSharedLibrary(
                        webViewContext.getApplicationInfo().sourceDir);
                ClassLoader clazzLoader = webViewContext.getClassLoader();

                Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()");
                loadNativeLibrary(clazzLoader);
                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);

                Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()");
                try {
                    return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY,
                            true, clazzLoader);
                } finally {
                    Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
                }
            } catch (ClassNotFoundException e) {
                Log.e(LOGTAG, "error loading provider", e);
                throw new AndroidRuntimeException(e);
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
            }
        } catch (MissingWebViewPackageException e) {
            // If the package doesn't exist, then try loading the null WebView instead.
            // If that succeeds, then this is a device without WebView support; if it fails then
            // swallow the failure, complain that the real WebView is missing and rethrow the
            // original exception.
            try {
                return (Class<WebViewFactoryProvider>) Class.forName(NULL_WEBVIEW_FACTORY);
            } catch (ClassNotFoundException e2) {
                // Ignore.
            }
            Log.e(LOGTAG, "Chromium WebView package does not exist", e);
            throw new AndroidRuntimeException(e);
        }
    }

    private static Context getWebViewContextAndSetProvider() {
        Application initialApplication = AppGlobals.getInitialApplication();
        try {
            WebViewProviderResponse response = null;
            try {
//IWebViewUpdateService 
                response = getUpdateService().waitForAndGetProvider();
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
            }
			...
            try {
                ActivityManagerNative.getDefault().addPackageDependency(
                        response.packageInfo.packageName);
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
            }
            // Fetch package info and verify it against the chosen package
            PackageInfo newPackageInfo = null;
            try {
                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);
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
            }

            // Validate the newly fetched package info, throws MissingWebViewPackageException on
            // failure
            verifyPackageInfo(response.packageInfo, newPackageInfo);

            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
                    "initialApplication.createApplicationContext");
            try {
                // Construct an app context to load the Java code into the current app.
                Context webViewContext = initialApplication.createApplicationContext(
                        newPackageInfo.applicationInfo,
                        Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
                sPackageInfo = newPackageInfo;
                return webViewContext;
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
            }
        } catch (RemoteException | PackageManager.NameNotFoundException e) {
            throw new MissingWebViewPackageException("Failed to load WebView provider: " + e);
        }
    }


    /** @hide */
    public static IWebViewUpdateService getUpdateService() {
        return IWebViewUpdateService.Stub.asInterface(
                ServiceManager.getService(WEBVIEW_UPDATE_SERVICE_NAME));
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值