• Android 处理WebView not install(源码分析定位)


    最近在处理游戏项目中Bugly上的bug, 发现一个WebView Installed问题,涉及FrameWork层中WebView的部分细节,记录下来。

    Bugly上捕捉的日志:

    08-27 06:20:16.860 17374 17374 E WebViewFactory: Chromium WebView package does not exist
    10708-27 06:20:16.860 17374 17374 E WebViewFactory: android.webkit.WebViewFactory$MissingWebViewPackageException: Failed to load WebView provider: No WebView installed
    10808-27 06:20:16.860 17374 17374 E WebViewFactory: at android.webkit.WebViewFactory.getWebViewContextAndSetProvider(WebViewFactory.java:270)
    10908-27 06:20:16.860 17374 17374 E WebViewFactory: at android.webkit.WebViewFactory.getProviderClass(WebViewFactory.java:330)
    11008-27 06:20:16.860 17374 17374 E WebViewFactory: at android.webkit.WebViewFactory.getProvider(WebViewFactory.java:194)
    11108-27 06:20:16.860 17374 17374 E WebViewFactory: at android.webkit.WebView.getFactory(WebView.java:2325)
    11208-27 06:20:16.860 17374 17374 E WebViewFactory: at android.webkit.WebView.ensureProviderCreated(WebView.java:2320)
    11308-27 06:20:16.860 17374 17374 E WebViewFactory: at android.webkit.WebView.setOverScrollMode(WebView.java:2379)
    11408-27 06:20:16.860 17374 17374 E WebViewFactory: at android.view.View.(View.java:4023)
    11508-27 06:20:16.860 17374 17374 E WebViewFactory: at android.view.View.(View.java:4146)
    11608-27 06:20:16.860 17374 17374 E WebViewFactory: at android.view.ViewGroup.(ViewGroup.java:580)
    11708-27 06:20:16.860 17374 17374 E WebViewFactory: at android.widget.AbsoluteLayout.(AbsoluteLayout.java:55)
    11808-27 06:20:16.860 17374 17374 E WebViewFactory: at android.webkit.WebView.(WebView.java:627)
    11908-27 06:20:16.860 17374 17374 E WebViewFactory: at android.webkit.WebView.(WebView.java:572)
    12008-27 06:20:16.860 17374 17374 E WebViewFactory: at android.webkit.WebView.(WebView.java:555)
    12108-27 06:20:16.860 17374 17374 E WebViewFactory: at android.webkit.WebView.(WebView.java:542)
    12208-27 06:20:16.860 17374 17374 E WebViewFactory: at java.lang.reflect.Constructor.newInstance0(Native Method)
    12308-27 06:20:16.860 17374 17374 E WebViewFactory: at java.lang.reflect.Constructor.newInstance(Constructor.java:430)
    12408-27 06:20:16.860 17374 17374 E WebViewFactory: at android.view.LayoutInflater.createView(LayoutInflater.java:645)
    12508-27 06:20:16.860 17374 17374 E WebViewFactory: at com.android.internal.policy.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:58)
    12608-27 06:20:16.860 17374 17374 E WebViewFactory: at android.view.LayoutInflater.onCreateView(LayoutInflater.java:717)
    12708-27 06:20:16.860 17374 17374 E WebViewFactory: at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:785)
    12808-27 06:20:16.860 17374 17374 E WebViewFactory: at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:727)
    12908-27 06:20:16.860 17374 17374 E WebViewFactory: at android.view.LayoutInflater.rInflate(LayoutInflater.java:858)
    13008-27 06:20:16.860 17374 17374 E WebViewFactory: at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:821)
    13108-27 06:20:16.860 17374 17374 E WebViewFactory: at android.view.LayoutInflater.rInflate(LayoutInflater.java:861)
    13208-27 06:20:16.860 17374 17374 E WebViewFactory: at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:821)
    13308-27 06:20:16.860 17374 17374 E WebViewFactory: at android.view.LayoutInflater.rInflate(LayoutInflater.java:861)
    13408-27 06:20:16.860 17374 17374 E WebViewFactory: at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:821)
    13508-27 06:20:16.860 17374 17374 E WebViewFactory: at android.view.LayoutInflater.rInflate(LayoutInflater.java:861)
    13608-27 06:20:16.860 17374 17374 E WebViewFactory: at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:821)
    13708-27 06:20:16.860 17374 17374 E WebViewFactory: at android.view.LayoutInflater.inflate(LayoutInflater.java:518)
    13808-27 06:20:16.860 17374 17374 E WebViewFactory: at android.view.LayoutInflater.inflate(LayoutInflater.java:426)
    13908-27 06:20:16.860 17374 17374 E WebViewFactory: at android.view.LayoutInflater.inflate(LayoutInflater.java:377)
    14008-27 06:20:16.860 17374 17374 E WebViewFactory: at android.view.View.inflate(View.java:21010)
    14108-27 06:20:16.860 17374 17374 E WebViewFactory: at com.duoku.platform.single.ui.DKVerifyActivity.onCreate(Native Method)
    14208-27 06:20:16.860 17374 17374 E WebViewFactory: at android.app.Activity.performCreate(Activity.java:6679)
    14308-27 06:20:16.860 17374 17374 E WebViewFactory: at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
    14408-27 06:20:16.860 17374 17374 E WebViewFactory: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2618)
    14508-27 06:20:16.860 17374 17374 E WebViewFactory: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726)
    14608-27 06:20:16.860 17374 17374 E WebViewFactory: at android.app.ActivityThread.-wrap12(ActivityThread.java)
    14708-27 06:20:16.860 17374 17374 E WebViewFactory: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477)
    14808-27 06:20:16.860 17374 17374 E WebViewFactory: at android.os.Handler.dispatchMessage(Handler.java:102)
    14908-27 06:20:16.860 17374 17374 E WebViewFactory: at android.os.Looper.loop(Looper.java:154)
    15008-27 06:20:16.860 17374 17374 E WebViewFactory: at android.app.ActivityThread.main(ActivityThread.java:6119)
    15108-27 06:20:16.860 17374 17374 E WebViewFactory: at java.lang.reflect.Method.invoke(Native Method)
    15208-27 06:20:16.860 17374 17374 E WebViewFactory: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:969)
    15308-27 06:20:16.860 17374 17374 E WebViewFactory: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:859)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    设备机型系统版本ROM
    NX16A8116KPAndroid 7.1.1,level 25NextBook/NX16A8116KP

    1.源码分析

    本篇源码,基于Android7.1 来介绍。

    WebViewFactory#getWebViewContextAndSetProvider开始看起:

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

        private static Context getWebViewContextAndSetProvider() {
            try {
                WebViewProviderResponse response = null;
                response = getUpdateService().waitForAndGetProvider();
           
                if (response.status != LIBLOAD_SUCCESS
                        && response.status != LIBLOAD_FAILED_WAITING_FOR_RELRO) {
    					// 报错点
                    throw new MissingWebViewPackageException("Failed to load WebView provider: "
                            + getWebViewPreparationErrorReason(response.status));
                }
                //... 省略部分源码
               } catch (RemoteException | PackageManager.NameNotFoundException e) {
                     throw new MissingWebViewPackageException("Failed to load WebView provider: " + e);
               }
        }
    	public static final int LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES = 4;
    	//根据status 获取error msg
    	private static String getWebViewPreparationErrorReason(int error) {
           switch (error) {
                case LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES:
                    return "No WebView installed"; // 匹配上报错信息
            }
            return "Unknown";
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    从上可知,调用WebViewUpdateService #waitForAndGetProvider() 返回status 是4,因此抛出MissingWebViewPackageException异常。

    App进程是通过跨进程方式来获取WebView有关数据,接下来看下WebView的Server端WebViewUpdateService

    接下来看下waitForAndGetProvider()

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

            @Override // 该方法是Binder 调用
            public WebViewProviderResponse waitForAndGetProvider() {
                //...
                return WebViewUpdateService.this.mImpl.waitForAndGetProvider();
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    从上可知,WebViewUpdateService 是通过WebViewUpdateServiceImpl对象来真正操作WebView有关数据的。
    这里涉及是代理方式,提供Sevice api 层,真正的操作是在Impl中。

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

        WebViewProviderResponse waitForAndGetProvider() {
            return mWebViewUpdater.waitForAndGetProvider();
        }
    
    • 1
    • 2
    • 3

    从上可知,WebViewUpdateServiceImpl对象是通过调用WebViewUpdater对象来进行有关操作。

    接下来看下,WebViewUpdateServiceImpl#WebViewUpdater静态内部类中:

            private static final int WAIT_TIMEOUT_MS = 1000; // KEY_DISPATCHING_TIMEOUT is 5000.
    		
            public WebViewProviderResponse waitForAndGetProvider() {
                PackageInfo webViewPackage = null;
                final long NS_PER_MS = 1000000;
                final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
                boolean webViewReady = false;
                int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS; // 默认是加载成功的状态
                synchronized (mLock) {
                    webViewReady = webViewIsReadyLocked();
                    while (!webViewReady) { // 这里通过周期性循环阻塞,等待webview load 状态
                        final long timeNowMs = System.nanoTime() / NS_PER_MS;
                        if (timeNowMs >= timeoutTimeMs) break;
                        try {
                            mLock.wait(timeoutTimeMs - timeNowMs);
                        } catch (InterruptedException e) {}
                        webViewReady = webViewIsReadyLocked();
                    }
                    // Make sure we return the provider that was used to create the relro file
                    webViewPackage = mCurrentWebViewPackage;
                    if (webViewReady) {
                    } else if (!mAnyWebViewInstalled) {
    				    //关键点:发现没有webview install 
                        webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
                    } else {
                         //当前WebView的relro 创建没有完成的状态,超时等待
                        webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO; 
                    }
                }
                if (!webViewReady) Slog.w(TAG, "creating relro file timed out");
                return new WebViewProviderResponse(webViewPackage, webViewStatus);
            }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    从上可知,会先检查WebView 是否load 状态,若是没有,则会等待1000毫秒,若是还没loady 成功,则会判定为LIBLOAD_FAILED_WAITING_FOR_RELRO
    若是没有install,则会判定为LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES

    app 端调用webview Server端返回的status 是LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES。因此,WebView Server端 中mAnyWebViewInstalled 是为false。

    接下来,全局检索下mAnyWebViewInstalled是如何赋值的。

    找到findPreferredWebViewPackage() 中会去对 mAnyWebViewInstalled赋值false。

            private PackageInfo findPreferredWebViewPackage() {
    		    //获取到webview 的信息列表
                ProviderAndPackageInfo[] providers =
                    getValidWebViewPackagesAndInfos(false /* onlyInstalled */);
    
                String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(mContext);
                //选择用户选择的webview
                for (ProviderAndPackageInfo providerAndPackage : providers) {
                    if (providerAndPackage.provider.packageName.equals(userChosenProvider)
                            && isInstalledPackage(providerAndPackage.packageInfo)
                            && isEnabledPackage(providerAndPackage.packageInfo)) {
                        return providerAndPackage.packageInfo;
                    }
                }
                //当用户没选择或者选择错误,使用安装可使用且注册条件是availableByDefault=true 的webview(列表中第一个)
                for (ProviderAndPackageInfo providerAndPackage : providers) {
                    if (providerAndPackage.provider.availableByDefault
                            && isInstalledPackage(providerAndPackage.packageInfo)
                            && isEnabledPackage(providerAndPackage.packageInfo)) {
                        return providerAndPackage.packageInfo;
                    }
                }
    
                //当没有可用或者安装的webview,使用默认availableByDefault=true的webview 。
                for (ProviderAndPackageInfo providerAndPackage : providers) {
                    if (providerAndPackage.provider.availableByDefault) {
                        return providerAndPackage.packageInfo;
                    }
                }
    			//没有availableByDefault=true的情况下,赋值false
                mAnyWebViewInstalled = false;
                throw new WebViewFactory.MissingWebViewPackageException(
                        "Could not find a loadable WebView package");
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    从上可知,当没有availableByDefault=true的情况下, mAnyWebViewInstalled 会被赋值false。

    接下来看下,getValidWebViewPackagesAndInfos()获取可用的Webview Provider包信息:

            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)) {// 这里传入onlyInstalled为false,添加全部
                            providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo));
                        }
                    } catch (NameNotFoundException e) {
                        // Don't add non-existent packages
                    }
                }
                return providers.toArray(new ProviderAndPackageInfo[providers.size()]);
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    SystemImplSystemInterface接口的实现类。

    接下来看下,SystemImplgetWebViewPackages()

    /frameworks/base/services/core/java/com/android/server/webkit/SystemImpl.java

        /**
         * Returns all packages declared in the framework resources as potential WebView providers.
         * @hide
         * */
        @Override
        public WebViewProviderInfo[] getWebViewPackages() {
            return mWebViewProviderPackages;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    通过检索,发现是SystemImpl构造函数中解析xml,对mWebViewProviderPackages进行赋值操作

       private final WebViewProviderInfo[] mWebViewProviderPackages;
     
        private SystemImpl() {
            int numFallbackPackages = 0;
            int numAvailableByDefaultPackages = 0;
            int numAvByDefaultAndNotFallback = 0;
            XmlResourceParser parser = null;
            List<WebViewProviderInfo> webViewProviders = new ArrayList<WebViewProviderInfo>();
            try {
                parser = AppGlobals.getInitialApplication().getResources().getXml(
                        com.android.internal.R.xml.config_webview_packages); // 解析xml 中Webview Provider信息
                XmlUtils.beginDocument(parser, TAG_START);
                while(true) {//循环解析
                    XmlUtils.nextElement(parser);
                    String element = parser.getName();
                    if (element == null) {
                        break;
                    }
                    if (element.equals(TAG_WEBVIEW_PROVIDER)) {//解析webviewproviders 标签
                        String packageName = parser.getAttributeValue(null, TAG_PACKAGE_NAME);
    					//...
                        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));
    				    //统计isFallback="true"的情况
                        if (currentProvider.isFallback) {
                            numFallbackPackages++;
    						// 不允许存在availableByDefault="false"的情况
                            if (!currentProvider.availableByDefault) {
                                throw new AndroidRuntimeException(
                                        "Each WebView fallback package must be available by default.");
                            }
    						// 最多允许一个isFallback="true"的情况
                            if (numFallbackPackages > 1) {
                                throw new AndroidRuntimeException(
                                        "There can be at most one WebView fallback package.");
                            }
                        }
    					//统计 availableByDefault="true"且isFallback="false"的情况
                        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();
            }
    		// 必须存在一个availableByDefault="true"且isFallback="false"的默认WebViewProvider
            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()]);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    从上可知,SystemImpl 是解析config_webview_packages.xml来获取到可用的webview Provider 信息的。
    新增的WebviewProvider 必须是availableByDefault="true"且isFallback="true"的注册,或者替换掉默认的webviewprovider

    接下来,看下Webview Provider的配置文件:
    /frameworks/base/core/res/res/xml/config_webview_packages.xml

    <webviewproviders>
        <!-- The default WebView implementation -->
        <webviewprovider description="Android WebView" packageName="com.android.webview" availableByDefault="true">
        </webviewprovider>
    	
    </webviewproviders>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    一般系统默认的是com.android.webview,处于首个availableByDefault="true"的webviewprovider的优先级最高。

    2.分析原因

    对该国产ROM的猜测:

    国产手机厂商肯定对config_webview_packages进行改动,添加了厂商自带的WebView

    查看方式

    先执行adb pull system/framework/framework.apk 先获取到包含该xml 的resource资源,
    接着执行aapt d xmltree framework-res.apk res/xml/config_webview_packages.xml命令便可打开该xml ,查看当前手机ROM 中webview的配置项。

    除此之外,也可以通过执行adb shell "pm list packages | grep webview"来查看当前手机安装了哪些webview包程序:
    在这里插入图片描述

    3.解决方案

    自定义WebView,重写setOverScrollMode(),捕捉该异常。

    public class FixWebView extends WebView {
        //...
        @Override
        public void setOverScrollMode(int mode) {
            try {
                super.setOverScrollMode(mode);
            } catch (Exception e) {
                // 若是发生webview 有关的crash ,则表明system webview 正在更新或者不存在(部分机型上可能存在该问题)
                if (e != null && e.getMessage()!=null&&e.getMessage().toLowerCase().contains("webview")) {
                    e.printStackTrace();
                } else {
                    throw e;
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
  • 相关阅读:
    软考 - 操作系统
    ChatGPT是否可以协助人们提高逻辑思维和问题解决能力?
    tomcat为什么要自定义类加载器?
    【原创】RockyLinux设置网络/网卡唤醒/NetworkManager设置网络唤醒
    FPGA 学习笔记:Vivado 2018.2 MicroBlaze Uartlite 配置
    真香警告,低代码平台免费获取攻略来了!
    Day_81-87 CNN卷积神经网络
    react router v6实现useHistory与自定义history设计思路
    一文讲清楚webpack和vite原理
    .NET 7 预览版 1 发布
  • 原文地址:https://blog.csdn.net/hexingen/article/details/126765233