多进程问题导致crash

ava.lang.RuntimeException: Using WebView from more than one process at once with the same data directory is not supported. https://crbug.com/558377
        at com.android.webview.chromium.WebViewChromiumAwInit.startChromiumLocked(WebViewChromiumAwInit.java:100)
        at com.android.webview.chromium.WebViewChromiumAwInitForP.startChromiumLocked(WebViewChromiumAwInitForP.java:3)
        at com.android.webview.chromium.WebViewChromiumAwInit.ensureChromiumStartedLocked(WebViewChromiumAwInit.java:180)
        at com.android.webview.chromium.WebViewChromiumAwInit.startYourEngines(WebViewChromiumAwInit.java:161)
        at com.android.webview.chromium.WebViewChromiumFactoryProvider.startYourEngines(WebViewChromiumFactoryProvider.java:217)
        at com.android.webview.chromium.WebViewChromium.init(WebViewChromium.java:44)
        at android.webkit.WebView.<init>(WebView.java:432)
        at android.webkit.WebView.<init>(WebView.java:358)
        at android.webkit.WebView.<init>(WebView.java:341)

三年前 Android 9 Android10出现的问题 经过setDataDirectorySuffix设置后 还是top1 的crash 。 多进程假如用到同一个webview目录就会导致crash, 看日志现象是不同的pid相同的进程名,猜想有些状况下存在两个相同包名的进程。处理办法也比较简单, 发动时发现目录现已被其他进程占用了 ,那就再加上一个pid后缀。

处理:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    try {
        if (application.getPackageName().equals(currentProcessName)) {
            File lockFile = new File(application.getDir("webview", Context.MODE_PRIVATE).getPath(), "webview_data.lock");
            if (lockFile.exists()) {
                RandomAccessFile lock = new RandomAccessFile(lockFile, "rw");
                //没有被其他进程锁住
                if (lock.getChannel().tryLock() != null) {
                    lockFile.delete()
                } else {
                    //被其他进程锁了
                    HAS_WEB_LOCK = true;
                    WEBVIEW_PATH = "webview";
                    WebView.setDataDirectorySuffix(application.getPackageName() + "_" + Process.myPid());
                }
            }
            //华为部分android 10 手机webview缓存目录为hws_webview
            if (Build.VERSION.SDK_INT >= 29 && ("HuaWei".equalsIgnoreCase(Build.BRAND) || "HONOR".equalsIgnoreCase(Build.BRAND))) {
                File hwLockFile = new File(application.getDir("hws_webview", Context.MODE_PRIVATE).getPath(), "webview_data.lock");
                if (hwLockFile.exists()) {
                    RandomAccessFile hwLock = new RandomAccessFile(hwLockFile, "rw");
                    //没有被其他进程锁住
                    if (hwLock.getChannel().tryLock() != null) {
                        lockFile.delete()
                    } else {
                        //被其他进程锁了
                        HAS_WEB_LOCK = true;
                        WEBVIEW_PATH = "hws_webview";
                        WebView.setDataDirectorySuffix(application.getPackageName() + "_" + Process.myPid());
                    }
                }
            }
        } else {
            WebView.setDataDirectorySuffix(currentProcessName);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

烘托问题导致crash

Crash排查系列第八篇|记录webview相关问题处理
仓库没有任何剖析思路 运用剖析工具查一波。

  1. 结合crash行为日志剖析出现场景。

有一次线上很多出现该问题,剖析行为日志路径大都是出现在BrowserActivity之后 检查load的url也是同一个,大概率是前端代码原因导致,前端排查发现加载了一个特别大的图片,下线图片后crash下降。

  1. 结合abort message ,backtrace ,java stacktrace, logcat排查问题。

不同行为路径下还少量存在该crash,这时就要去看详细crash是如何触发的了。

Crash排查系列第八篇|记录webview相关问题处理

Crash排查系列第八篇|记录webview相关问题处理

  1. 首先经过上面信息咱们能够复制粘贴到浏览器搜索一波。
  2. 源码盯梢 chromium.googlesource.com/chromium/sr…

Crash排查系列第八篇|记录webview相关问题处理

看delegate->onRenderProcessGone办法回来的不同值做不同的处理,这个办法最终会调用到

android.webkit.WebViewClient的onRenderProcessGone办法

Crash排查系列第八篇|记录webview相关问题处理

看注释webview 烘托进程退出会通知到该办法。能够用loadUrl(“chrome://crash”)测试这个case .多个webview同享render process 都需要处理不仅仅是loadUrl(“chrome://crash”)的这个webview。

这边假如不处理默许回来false render process挂了会导致使用进程也挂了。

处理:

对项目中的Webview client复现该办法 调用后上报反常埋点及当时加载url 处理webview 回来true。

但是用loadUrl(“chrome://crash”)做测试时 并没有回调该办法而直接crash。 后续对framwork层android.webkit.WebViewClient进行调试 发现这边确实断点到了 没有走子类的办法。发现是两个webview不一样。原来是咱们项目做了webview缓存池的一个功用。会提早构建下一个webview 但是这个webview的WebViewClient用是默许的WebViewClient所以回来的是false。 回到上面触发crash代码片断 确实是一个循环去触发AwContents的onRenderProcessGone 发现有没处理的就crash了。最后针对提早构建的webview也去设置一个自己的WebViewClient 。建立反常url报表

apk&so 反常不兼容导致crash

89.0.4389.90版别导致 crash

Android体系的WebView错误更新,致使很多使用溃散 zhuanlan.zhihu.com/p/359482553

so 架构不匹配导致 crash

Caused by: java.lang.RuntimeException: Cannot load WebView
at org.chromium.android_webview.AwBrowserProcess.a(PG:20)
at com.android.webview.chromium.WebViewChromiumFactoryProvider.a(PG:81)
at com.android.webview.chromium.WebViewChromiumFactoryProvider.<init>(PG:12)
at com.android.webview.chromium.WebViewChromiumFactoryProviderForOMR1.<init>(PG:1)
at com.android.webview.chromium.WebViewChromiumFactoryProviderForOMR1.create(PG:1)
... 39 more
Caused by: Qy0
at org.chromium.base.library_loader.LibraryLoader.a(PG:40)
at org.chromium.base.library_loader.LibraryLoader.a(PG:16)
at org.chromium.android_webview.AwBrowserProcess.a(PG:16)
... 43 more
Caused by: java.lang.UnsatisfiedLinkError: dlopen failed: "/data/app/com.android.chrome-b7SMbfSB6LfJn2_2Ai7yHA==/base.apk!/lib/armeabi-v7a/libmonochrome.so" is 32-bit instead of 64-bit
at java.lang.Runtime.loadLibrary0(Runtime.java:1016)
at java.lang.System.loadLibrary(System.java:1657)
at org.chromium.base.library_loader.LibraryLoader.a(PG:31)
... 45 more

处理: 创立webview的时候做try catch 依据仓库进行判别 降级提示用户webview版别不兼容,无法运用,辅导和建议进行升级

webview 预创立提速

发动优化发现webview 预创立耗时特别明显,现已到了耗时使命top1

trace结合xposed咱们很简单做到竞品webview 发动剖析,比照效果,比如一些app能够把解析webview apk的逻辑放到子线程,咱们也能够对这个方案进行尝试。

Crash排查系列第八篇|记录webview相关问题处理
把上面的代码放到子线程 ,当然有个条件如下图,子线程加载要和主线程使命A一起,因为主线程webview使命肯定是要加载webview apk后执行的。 线上测试下来能平均优化100ms,抱负状况有200多ms, 当然业务假如答应在运用时再创立webview 更能提高发动速度,不过第一次翻开h5页面体会就会受到影响。

Crash排查系列第八篇|记录webview相关问题处理

处理Webview crash 相关的报表

除了一般处理crash的一些报表 比如设备维度,体系版别维度等报表,webview crash有额定几个维度的报表能帮助咱们快速定位问题。

  • crash时load url的报表

    • 还有一些crash时是和加载的url有关的,这部分客户端能做的是做一个url crash的趋势报表 发现反常及时告警推动h5同学去处理。
  • webview.apk version维度报表

比如之前89.0.4389.90的webview.apk版别的问题 就很简单经过这个报表发现。

Crash排查系列第八篇|记录webview相关问题处理

public static List<String> getWebviewApkVersions(Application application) {
    ArrayList<String> versions = new ArrayList<>();
    if (application == null) {
        return versions;
    }
    PackageManager packageManager = application.getPackageManager();
    ArrayList<String> webviewPackages = new ArrayList<>();
    webviewPackages.add("com.google.android.webview");
    webviewPackages.add("com.android.webview");
    if ("xiaomi".equalsIgnoreCase(Build.BRAND) || "redmi".equalsIgnoreCase(Build.BRAND)) {
        webviewPackages.add("com.mi.webkit.core");
    }
    if ("huawei".equalsIgnoreCase(Build.BRAND) || "HONOR".equalsIgnoreCase(Build.BRAND)) {
        webviewPackages.add("com.huawei.webview");
    }
    if (packageManager != null) {
        for (String packageName : webviewPackages) {
            PackageInfo packageInfo;
            try {
                packageInfo = packageManager.getPackageInfo(packageName, 0);
                if (packageInfo != null) {
                    versions.add(packageName + "-" + packageInfo.versionName);
                }
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    return versions;

比如这个版别肯定是有问题的。 这部分能够去检查厂商,体系版别,行为日志尝试复现,进行问题反应。