草庐IT

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

灯塔@kuaidao 2023-04-03 原文

应用场景

应用内有两个位置用到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多进程崩溃的问题

有关解决 Android WebView 多进程导致App崩溃的更多相关文章

  1. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

    为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

  2. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  3. ruby - 通过 ruby​​ 进程共享变量 - 2

    我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是

  4. Ruby Readline 在向上箭头上使控制台崩溃 - 2

    当我在Rails控制台中按向上或向左箭头时,出现此错误:irb(main):001:0>/Users/me/.rvm/gems/ruby-2.0.0-p247/gems/rb-readline-0.4.2/lib/rbreadline.rb:4269:in`blockin_rl_dispatch_subseq':invalidbytesequenceinUTF-8(ArgumentError)我使用rvm来管理我的ruby​​安装。我正在使用=>ruby-2.0.0-p247[x86_64]我使用bundle来管理我的gem,并且我有rb-readline(0.4.2)(人们推荐的最少

  5. ruby-on-rails - 每次我尝试部署时,我都会得到 - (gcloud.preview.app.deploy) 错误响应 : [4] DEADLINE_EXCEEDED - 2

    我是Google云的新手,我正在尝试对其进行首次部署。我的第一个部署是RubyonRails项目。我基本上是在关注thisguideinthegoogleclouddocumentation.唯一的区别是我使用的是我自己的项目,而不是他们提供的“helloworld”项目。这是我的app.yaml文件runtime:customvm:trueentrypoint:bundleexecrackup-p8080-Eproductionconfig.ruresources:cpu:0.5memory_gb:1.3disk_size_gb:10当我转到我的项目目录并运行gcloudprevie

  6. ruby-on-rails - 如何重命名或移动 Rails 的 README_FOR_APP - 2

    当我在我的Rails应用程序根目录中运行rakedoc:app时,API文档是使用/doc/README_FOR_APP作为主页生成的。我想向该文件添加.rdoc扩展名,以便它在GitHub上正确呈现。更好的是,我想将它移动到应用程序根目录(/README.rdoc)。有没有办法通过修改包含的rake/rdoctask任务在我的Rakefile中执行此操作?是否有某个地方可以查找可以修改的主页文件的名称?还是我必须编写一个新的Rake任务?额外的问题:Rails应用程序的两个单独文件/README和/doc/README_FOR_APP背后的逻辑是什么?为什么不只有一个?

  7. 屏幕录制为什么没声音?检查这2项,轻松解决 - 2

    相信很多人在录制视频的时候都会遇到各种各样的问题,比如录制的视频没有声音。屏幕录制为什么没声音?今天小编就和大家分享一下如何录制音画同步视频的具体操作方法。如果你有录制的视频没有声音,你可以试试这个方法。 一、检查是否打开电脑系统声音相信很多小伙伴在录制视频后会发现录制的视频没有声音,屏幕录制为什么没声音?如果当时没有打开音频录制,则录制好的视频是没有声音的。因此,建议在录制前进行检查。屏幕上没有声音,很可能是因为你的电脑系统的声音被禁止了。您只需打开电脑系统的声音,即可录制音频和图画同步视频。操作方法:步骤1:点击电脑屏幕右下侧的“小喇叭”图案,在上方的选项中,选择“声音”。 步骤2:在“声

  8. 【高数】用拉格朗日中值定理解决极限问题 - 2

    首先回顾一下拉格朗日定理的内容:函数f(x)是在闭区间[a,b]上连续、开区间(a,b)上可导的函数,那么至少存在一个,使得:通过这个表达式我们可以知道,f(x)是函数的主体,a和b可以看作是主体函数f(x)中所取的两个值。那么可以有,  也就意味着我们可以用来替换 这种替换可以用在求某些多项式差的极限中。方法: 外层函数f(x)是一致的,并且h(x)和g(x)是等价无穷小。此时,利用拉格朗日定理,将原式替换为 ,再进行求解,往往会省去复合函数求极限的很多麻烦。使用要注意:1.要先找到主体函数f(x),即外层函数必须相同。2.f(x)找到后,复合部分是等价无穷小。3.要满足作差的形式。如果是加

  9. ruby - 使用 postgres.app 在 rvm 下要求 pg 时出错 - 2

    我正在使用Postgres.app在OSX(10.8.3)上。我已经修改了我的PATH,以便应用程序的bin文件夹位于所有其他文件夹之前。Rammy:~phrogz$whichpg_config/Applications/Postgres.app/Contents/MacOS/bin/pg_config我已经安装了rvm并且可以毫无错误地安装pggem,但是当我需要它时我得到一个错误:Rammy:~phrogz$gem-v1.8.25Rammy:~phrogz$geminstallpgFetching:pg-0.15.1.gem(100%)Buildingnativeextension

  10. 深度学习部署:Windows安装pycocotools报错解决方法 - 2

    深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal

随机推荐