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.<init>(View.java:4023)
11508-27 06:20:16.860 17374 17374 E WebViewFactory: at android.view.View.<init>(View.java:4146)
11608-27 06:20:16.860 17374 17374 E WebViewFactory: at android.view.ViewGroup.<init>(ViewGroup.java:580)
11708-27 06:20:16.860 17374 17374 E WebViewFactory: at android.widget.AbsoluteLayout.<init>(AbsoluteLayout.java:55)
11808-27 06:20:16.860 17374 17374 E WebViewFactory: at android.webkit.WebView.<init>(WebView.java:627)
11908-27 06:20:16.860 17374 17374 E WebViewFactory: at android.webkit.WebView.<init>(WebView.java:572)
12008-27 06:20:16.860 17374 17374 E WebViewFactory: at android.webkit.WebView.<init>(WebView.java:555)
12108-27 06:20:16.860 17374 17374 E WebViewFactory: at android.webkit.WebView.<init>(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)
设备机型 系统版本 ROM
NX16A8116KP Android 7.1.1,level 25 NextBook/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";
    }

从上可知,调用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();
        }

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

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

    WebViewProviderResponse waitForAndGetProvider() {
    
    
        return mWebViewUpdater.waitForAndGetProvider();
    }

从上可知,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);
        }

从上可知,会先检查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");
        }

从上可知,当没有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()]);
        }

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;
    }

通过检索,发现是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()]);
    }

从上可知,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>

一般系统默认的是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;
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/hexingen/article/details/126765233
今日推荐