Android 7.1 WebView 实现方式选择

简介: 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));
    }
相关文章
|
API Android开发 数据安全/隐私保护
解决android webview 加载http url 失败 net::ERR_CLEARTEXT_NOT_PERMITTED 错误
解决android webview 加载http url 失败 net::ERR_CLEARTEXT_NOT_PERMITTED 错误
1136 0
|
3月前
|
程序员 开发工具 Android开发
Android|WebView 禁止长按,限制非白名单域名的跳转层级
如何限制 WebView 仅域名白名单网址能随意跳转,并禁用长按选择文字。
50 2
|
7月前
|
安全 JavaScript 前端开发
kotlin开发安卓app,JetPack Compose框架,给webview新增一个按钮,点击刷新网页
在Kotlin中开发Android应用,使用Jetpack Compose框架时,可以通过添加一个按钮到TopAppBar来实现WebView页面的刷新功能。按钮位于右上角,点击后调用`webViewState?.reload()`来刷新网页内容。以下是代码摘要:
|
7月前
|
JavaScript 前端开发 Android开发
kotlin安卓在Jetpack Compose 框架下使用webview , 网页中的JavaScript代码如何与native交互
在Jetpack Compose中使用Kotlin创建Webview组件,设置JavaScript交互:`@Composable`函数`ComposableWebView`加载网页并启用JavaScript。通过`addJavascriptInterface`添加`WebAppInterface`类,允许JavaScript调用Android方法如播放音频。当页面加载完成时,执行`onWebViewReady`回调。
|
7月前
|
Web App开发 移动开发 前端开发
52. 【Android教程】网页视图:WebView
52. 【Android教程】网页视图:WebView
110 1
|
6月前
|
Web App开发 JavaScript 前端开发
Android端使用WebView注入一段js代码实现js调用android
Android端使用WebView注入一段js代码实现js调用android
154 0
|
7月前
|
安全 网络安全 API
kotlin安卓开发JetPack Compose 如何使用webview 打开网页时给webview注入cookie
在Jetpack Compose中使用WebView需借助AndroidView。要注入Cookie,首先在`build.gradle`添加WebView依赖,如`androidx.webkit:webkit:1.4.0`。接着创建自定义`ComposableWebView`,通过`CookieManager`设置接受第三方Cookie并注入Cookie字符串。最后在Compose界面使用这个自定义组件加载URL。注意Android 9及以上版本可能需要在网络安全配置中允许第三方Cookie。
|
8月前
|
XML Android开发 数据格式
安卓和webview交互
安卓和webview交互
64 0
|
8月前
|
XML Android开发 数据格式
安卓和webview交互
安卓和webview交互
109 1
|
小程序 Android开发 iOS开发
在钉钉小程序中安卓无法打开webview
在钉钉小程序中安卓无法打开webview
235 1