最近在处理游戏项目中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)
设备机型 | 系统版本 | ROM |
---|---|---|
NX16A8116KP | Android 7.1.1,level 25 | NextBook/NX16A8116KP |
本篇源码,基于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()]);
}
SystemImpl
是SystemInterface
接口的实现类。
接下来看下,SystemImpl
中getWebViewPackages()
:
/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的优先级最高。
对该国产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包程序:
自定义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;
}
}
}
}