解决 Android WebView 多进程导致App崩溃

应用场景

应用内有两个位置用到WebView加载页面,具体处理逻辑不能通用。分别扩展了WebView了。应用内独立页面使用Fragment来展示(单Activity架构),应用提供切换语言功能。

一、WebView内核bug

具体路径:
进入app–>设置-切换语言(应用界面重新加载)-再次进入设置->跳转到WebViewFragment展示H5。随便操作滑动。退出到上一页。重复该操作,会引发应用crash

MI 10 输出error 日志。
请添加图片描述

] Failed to create directory: /data/user/0/com.codeview.miniparty/cache/WebView/Default/HTTP Cache/Code Cache/js
D  Compat change id reported: 171228096; UID 10728; state: ENABLED
I  QUALCOMM build                   : db3d445dbc, Ia06b22fa1a
   Build Date                       : 10/04/21
   OpenGL ES Shader Compiler Version: EV031.32.02.16
   Local Branch                     : 
   Remote Branch                    : 
   Remote Branch                    : 
   Reconstruct Branch               : 
I  Build Config                     : S P 10.0.7 AArch64
I  Driver Path                      : /vendor/lib64/egl/libGLESv2_adreno.so
W  Application attempted to call on a destroyed WebView
   java.lang.Throwable
    at org.chromium.android_webview.AwContents.m(chromium-TrichromeWebViewGoogle6432.aab-stable-530410534:10)
    at com.android.webview.chromium.WebViewChromium.addJavascriptInterface(chromium-TrichromeWebViewGoogle6432.aab-stable-530410534:25)
    at android.webkit.WebView.addJavascriptInterface(WebView.java:1928)
    at miui.contentcatcher.sdk.utils.WebViewUtils$NativeWebViewUtils.addJavascriptInterface(WebViewUtils.java:244)
    at miui.contentcatcher.sdk.utils.WebViewUtils.initWebViewJsInterface(WebViewUtils.java:157)
    at miui.contentcatcher.InterceptorProxy$1.run(InterceptorProxy.java:193)
    at android.os.Handler.handleCallback(Handler.java:938)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loopOnce(Looper.java:210)
    at android.os.Looper.loop(Looper.java:299)
    at android.app.ActivityThread.main(ActivityThread.java:8293)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:556)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1045)

网上找到chrome 官方issue问题。
https://bugs.chromium.org/p/chromium/issues/detail?id=558377
该问题一直追踪到2021年也并没有给出具体的解决方案。

二 快速操作界面开关,导致WebView异常,引发应用闪退

在红米K40上(Android 12)显示
请添加图片描述

Build fingerprint: 'Verizon/kltevzw/kltevzw:5.0/LRX21T/G900VVRU2BOE1:user/release-keys'
Revision: '14'
ABI: 'arm'
pid: 30968, tid: 30968, name: com.myapp >>> com.myapp <<<
signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
Abort message: '[FATAL:jni_android.cc(295)] Check failed: false. Please include Java exception stack in crash report
'
r0 00000000 r1 000078f8 r2 00000006 r3 00000000
r4 b6f5c114 r5 00000006 r6 0000000b r7 0000010c
r8 b6f3be04 r9 bec21408 sl 00000000 fp bec213cc
ip 000078f8 sp bec20ee0 lr b6ee5fd1 pc b6f09970 cpsr 600f0010

backtrace:
#00 pc 00037970 /system/lib/libc.so (tgkill+12)
#01 pc 00013fcd /system/lib/libc.so (pthread_kill+52)
#02 pc 00014beb /system/lib/libc.so (raise+10)
#03 pc 00011531 /system/lib/libc.so (__libc_android_abort+36)
#04 pc 0000fcbc /system/lib/libc.so (abort+4)
#05 pc 002a7569 /data/app/com.google.android.webview-2/lib/arm/libwebviewchromium.so 

这个问题在stackoverflows上找到另一个解决方案
https://stackoverflow.com/questions/31416568/could-someone-help-me-with-this-crash-report

这个问题更像是应用内使用的场景,webView在Fragment中,快速打开关闭Fragment导致应用Crash。

在Application的AccachBaseContext(base:Context)中进行处理multiProcess

    override fun attachBaseContext(base: Context?) {
        super.attachBaseContext(base)
        WebViewMultiProcessPatch.apply {
            setDirectorySuffix(
                isSameProcess(this@SkyCastleApplication),
                getProcessName(this@SkyCastleApplication)
            )
        }
    }

新建一个WebViewMultiProcessPatch单例,用来做一些逻辑判断

/**
 * 
 * Date:   2022/11/29 19:00
 * Description:多进程闪退
 */


object WebViewMultiProcessPatch {

    /**
     * fix:Using WebView from more than one process at once with the same data directory is not supported
     * https://www.yisu.com/zixun/445583.html
     */
    fun tryLockOrRecreateFile(context: Context, processName: String) {
        val filePath =
            context.dataDir.absolutePath + "/${processName}/app_webview/webview_data.lock"
        File(filePath).let {
            if (it.exists()) {
                try {
                    val tryLock = RandomAccessFile(it, "rw").channel.tryLock()
                    if (tryLock != null) {
                        tryLock.close()
                    } else {
                        createLockFile(it, it.delete())
                    }
                } catch (e: Exception) {
                    e.printStackTrace()
                    var delete = false
                    if (it.exists()) {
                        delete = it.delete()
                    }
                    createLockFile(it, delete)
                }
            }
        }
    }

    private fun createLockFile(file: File, delete: Boolean) {
        try {
            if (delete && !file.exists()) {
                file.createNewFile()
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    /**
     * fix:Using WebView from more than one process at once with the same data directory is not supported
     * https://www.jianshu.com/p/f3ccc065f632
     */
    fun setDirectorySuffix(isSameProcess: Boolean, processName: String?) {
        if (!isSameProcess) {
            //see https://developer.android.google.cn/about/versions/pie/android-9.0-changes-28#web-data-dirs
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && !processName.isNullOrBlank()) {
                WebView.setDataDirectorySuffix(processName)
            }
        }
    }


    fun isSameProcess(context: Context): Boolean {
        return getProcessName(context) == getPackageName(context)
    }

    fun getProcessName(cxt: Context): String? {
        val pid = Process.myPid()
        val am = cxt.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
        val runningApps = am.runningAppProcesses ?: return null
        for (procInfo in runningApps) {
            if (procInfo.pid == pid) {
                return procInfo.processName
            }
        }
        return null
    }
    private fun getPackageName(context: Context): String? {
        return context.packageName
    }

}

总结

Android 不仅需要对不同品牌的设备适配,还需要针对不同的系统版本做适配。会有比较多的问题定位不到具体原因。这种情况不但要平时多多积累,更要掌握解决问题的思路。

引用

  1. Android webView
  2. Android 9.0 行为变更
  3. Android如何解决WebView多进程崩溃的问题
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>