Using WebView from more than one process

这篇具有很好参考价值的文章主要介绍了Using WebView from more than one process。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。
专注于分享各领域原创系列文章 ,擅长java后端、移动开发、商业变现、人工智能等,希望大家多多支持。
未经允许不得转载

Using WebView from more than one process,问题记录,webview

一、导读

我们继续总结学习遇到的问题,温故知新。

今天遇到一个线上问题,启动就闪退,比较坑,在此做一个记录,防止掉坑。

本文记录一次bug解决的过程,

Using WebView from more than one process

二、概览

今天将 targetSdkVersion 的版升级到了29,出现了一些奇怪的报错,日志如下

Fatal Exception: java.lang.RuntimeException: Using WebView from more than one process at once with the same data directory is not supported. 
https://crbug.com/558377 : Current process com.xx.xxapp(pid 13862), lock owner com.xx.xx.xxAPP (pid 13559)
       at org.chromium.android_webview.AwDataDirLock.b(AwDataDirLock.java:27)
       at as0.i(as0.java:30)
       at Zr0.run(Zr0.java:2)
       at android.os.Handler.handleCallback(Handler.java:883)
       at android.os.Handler.dispatchMessage(Handler.java:100)
       at android.os.Looper.loop(Looper.java:224)
       at android.app.ActivityThread.main(ActivityThread.java:7520)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)

三、问题过程

我们查看文档发现, google 文档
在android 9.0系统上如果多个进程使用WebView需要使用官方提供的api在子进程中给webview的数据文件夹设置后缀:

如果不设置,则会报错,不过这个影响范围有限,影响范围: Android 9及以上 且targetSdkVersion >= 28

    Starting Android Pie (API 28), Google isn't allowing using a single WebView instance in 2 different processes.
    
    WebView.setDataDirectorySuffix(suffix);

官方提供方案

protected void attachBaseContext(Context base) {
    mApplicationContext = base;
    webViewSetPath(this);
  }  


public void webViewSetPath(Context context) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        String processName = SpecialUtils.getCurProcessName(context);
        
        // 根据进程名称,设置多个目录
        if(!CommonConstant.NEW_PACKAGE_NAME.equals(processName)){
            WebView.setDataDirectorySuffix(getString(processName,"这里隐藏名字,自己设置个目录"));
        }
    }
}

public String getString(String processName, String defValue) {
    return TextUtils.isEmpty(processName) ? defValue : processName;
}

通过使用官方提供的方法后,实际在项目中运用 application中设置多个存储目录,虽然能减少问题发生的次数,但从bugly后台依然能收到此问题的大量崩溃信

源码追踪

那么这个问题发生的原因究竟是什么?一起来分析下抛出这个异常的逻辑吧
https://chromium.googlesource.com/chromium/src/+/refs/heads/main/android_webview/java/src/org/chromium/android_webview/AwDataDirLock.java#126

从源码分析调用链最终调用到了AwDataDirLock类中的lock方法

abstract class AwDataDirLock {

    static void lock(final Context appContext) {
        try (ScopedSysTraceEvent e1 = ScopedSysTraceEvent.scoped("AwDataDirLock.lock");
             StrictModeContext ignored = StrictModeContext.allowDiskWrites()) {
            if (sExclusiveFileLock != null) {
                我们已经调用了lock(),并在此过程中成功获取了锁
                return;
            }
            
            如果我们已经调用了lock(),但没有成功获得锁,则可能应用程序捕获到异常,进行自动重启。
            if (sLockFile == null) {
                String dataPath = PathUtils.getDataDirectory();
                File lockFile = new File(dataPath, EXCLUSIVE_LOCK_FILE);
                try {
                    // Note that the file is kept open intentionally.
                    sLockFile = new RandomAccessFile(lockFile, "rw");
                } catch (IOException e) {
                    throw new RuntimeException("Failed to create lock file " + lockFile, e);
                }
            }
            
            对webview数据目录中的webview_data.lock文件在for循环中尝试加锁16for (int attempts = 1; attempts <= LOCK_RETRIES; ++attempts) {
                try {
                    sExclusiveFileLock = sLockFile.getChannel().tryLock();
                } catch (IOException e) {
                }
                
                如果加锁成功会将该进程id和进程名写入到文件
                if (sExclusiveFileLock != null) {
                    writeCurrentProcessInfo(sLockFile);
                    return;
                }
                if (attempts == LOCK_RETRIES) break;
                try {
                    Thread.sleep(LOCK_SLEEP_MS);
                } catch (InterruptedException e) {
                }
            }
            
            如果加锁失败则会抛出异常
            // Using WebView from more than one process 
            String error = getLockFailureReason(sLockFile);
            boolean dieOnFailure = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
                    && appContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P;
            if (dieOnFailure) {
                throw new RuntimeException(error);
            } else {
            }
        }
    }
}

分析了原因,我们来看看解决思路,我们可以在应用启动时对该文件尝试加锁,如果加锁失败就删除该文件并重新创建,加锁成功就立即释放锁,这样当系统尝试加锁时理论上是可以加锁成功的。
通过检查目标目录的文件锁,如果能够获得到锁,就表明无异常;如果获取不到文件锁,再次重新设置存储目录。


public class WebViewUtil {

    public static void handleWebViewDir(Context context) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
            return;
        }
        try {
            String suffix = "";
            String processName = getCurProcessName(context);
            if (!TextUtils.equals(context.getPackageName(), processName)) {//判断不等于默认进程名称
                suffix = TextUtils.isEmpty(processName) ? context.getPackageName() : processName;
                WebView.setDataDirectorySuffix(suffix);
                suffix = "_" + suffix;
            }
            tryLockOrRecreateFile(context,suffix);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @TargetApi(Build.VERSION_CODES.P)
    private static void tryLockOrRecreateFile(Context context, String suffix) {
        String sb = context.getDataDir().getAbsolutePath() +
                "/app_webview"+suffix+"/webview_data.lock";
        File file = new File(sb);
        if (file.exists()) {
            try {
                FileLock tryLock = new RandomAccessFile(file, "rw").getChannel().tryLock();
                if (tryLock != null) {
                    tryLock.close();
                } else {
                    createFile(file, file.delete());
                }
            } catch (Exception e) {
                e.printStackTrace();
                boolean deleted = false;
                if (file.exists()) {
                    deleted = file.delete();
                }
                createFile(file, deleted);
            }
        }
    }

    private static void createFile(File file, boolean deleted){
        try {
            if (deleted && !file.exists()) {
                file.createNewFile();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static String getCurProcessName(Context context) {
        int pid = android.os.Process.myPid();
        ActivityManager activityManager = (ActivityManager) context
                .getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager
                .getRunningAppProcesses();
        if (appProcesses == null) {
            return null;
        }
        for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
            if (appProcess == null) {
                continue;
            }
            if (appProcess.pid == pid) {
                return appProcess.processName;
            }
        }
        return null;
    }

}

但是这样上线后发现还有问题,原因是不同机型,目录可能不一样,
我们自己使用debug包查看webview数据目录发现系统默认添加了进程名后缀,这是由于用户更新了手机系统导致,
使用华为mate20X测试调用 WebView.selDataDirecloySufx 自定义后缀已不生效,会默认强制指定后缀为进程名,
另外还发现部分华为手机直接将webview目录名app webview改为了app hws webview。

Using WebView from more than one process,问题记录,webview
综上所述,我们需要针对不同手机系统遍历可能的文件路径,最新解决代码如下:

```java

    public static void handleWebViewDir(Context context) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
            return;
        }

        String webViewDir = "/app_webview";
        String huaweiWebViewDir = "/app_hws_webview";
        String lockFile = "/webview_data.lock";
        
        try {
            xxx
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @TargetApi(Build.VERSION_CODES.P)
    private static void tryLockOrRecreateFile(String path) {
        File file = new File(path);
        if (file.exists()) {
            try {
                FileLock tryLock = (new RandomAccessFile(file, "rw")).getChannel().tryLock();
                if (tryLock != null) {
                    tryLock.close();
                } else {
                    createFile(file, file.delete());
                }
            } catch (Exception e) {
                boolean deleted = false;
                if (file.exists()) {
                    deleted = file.delete();
                }

                createFile(file, deleted);
            }
        }

    }

    private static void createFile(File file, boolean deleted) {
        try {
            if (deleted && !file.exists()) {
                boolean var2 = file.createNewFile();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

然后在application的oncreate方法中调用 handleWebViewDir();

参考文章:
文章 1
文章 2
文章 3

四、 推荐阅读

Java 专栏

SQL 专栏

数据结构与算法

Android学习专栏

未经允许不得转载

Using WebView from more than one process,问题记录,webview文章来源地址https://www.toymoban.com/news/detail-838401.html

到了这里,关于Using WebView from more than one process的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【解决】RuntimeError: Boolean value of Tensor with more than one value is ambiguous

    在用pytorch进行损失函数计算时,报错误: 翻译过来就是说: 具有多个值的张量的布尔值不明确  我是这报错: 啥意思?,你问我,我也不知道呀!、、、  错误原因分析: 其实是,因为我损失函数调用时没有初始化,所以导致报错 其实我是初始化了,但是因为没有+(),

    2024年02月16日
    浏览(48)
  • keil5版本时“error: L6235E: More than one section matches selector - cannot all be FIRST/LAST.”

    前言:在使用keil 5版本时,创建工程后稍不留神会出现问题“.Objectsproject.sct(7): error: L6235E: More than one section matches selector - cannot all be FIRST/LAST.”     保姆教程!! 问题描述: 出现下类问题,无疑是指你的启动文件不止一个,例如“startup_stm32f10x_md.s”,就是创建工程时,

    2024年02月16日
    浏览(39)
  • ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.al

    这个错误通常发生在你在使用numpy数组作为if语句的条件时。在这种情况下,Python会尝试使用该数组中的所有元素来确定if语句的真假。由于numpy数组可能包含多个元素,因此Python会抛出ValueError错误,因为它不知道如何处理多个元素的数组。 解决方法就是使用 a.all() or a.any() 替

    2024年02月16日
    浏览(46)
  • 中国工科研究生200多篇英文论文中最常见的习惯(The Most Common Habits from more than 200 English Papers written by Gradua)

    原文地址:http://staff.ustc.edu.cn/~jpq/writing/The%20Most%20Common%20Habits.pdf 本文介绍了中国作家在200多篇英语科技论文中观察到的一些最常见的汉英习惯。这些习惯会被解释,在大多数情况下,来自实际论文的示例文本会与首选文本一起给出。试图解释如何纠正和防止此类错误。在某

    2024年02月08日
    浏览(44)
  • 彻底解决ES 数据查询 from + size must be less than or equal to:xxx 问题

    ES分页查询时出现超过一万页就爆出这个错误:Result window is too large, from + size must be less than or equal to: [10000] but… 该错误是由于es默认设置最大页数为一万的原因导致的,这样设置也是为了防止OOM。 第一种解决方式: 防止这个错误出现是设置 index.max_result_window的值。但是这种

    2024年02月14日
    浏览(38)
  • Centos 报错 Repository extras is listed more than once in the configuration

    使用 yum update -y 报错 Repository extras is listed more than once in the configuration 2020 年 12 月 8 号,CentOS 官方宣布了停止维护 CentOS Linux 的计划,并推出了 CentOS Stream 项目,CentOS Linux 8 作为 RHEL 8 的复刻版本,生命周期缩短,于 2021 年 12 月 31 日停止更新并停止维护(EOL),更多的信息可

    2024年02月12日
    浏览(35)
  • ES5节点假死,内核日志报INFO: task blocked for more than 120 seconds.

    es集群里有一台机器,突然cpu load飙到 21左右(8core cpu),但是cpu使用率会变成0,且同时io 等使用率全部变为0.这种状态不可以自己恢复,除非重启。es没有判断出节点有问题,整个集群不可以访问,所有操作都超时,包括cat集群信息等接口,直到重启机器。这种情况下节点已

    2024年02月06日
    浏览(40)
  • Repository docker-ce-test is listed more than once in the configuration

    这个消息表明,在你的 CentOS 系统的 YUM 软件源配置中, docker-ce-stable 、 docker-ce-stable-source 和 docker-ce-test 这几个仓库被列出了多次。这通常发生在 /etc/yum.repos.d/ 目录下的 YUM 配置文件中,相同的仓库被重复添加。 这种情况可能不会直接影响你安装软件包的能力,但它可能导致

    2024年01月21日
    浏览(34)
  • RuntimeWarning: More than 20 figures have been opened. Figures created through the pyplot interface

    使用 matplotlib 绘制多幅图出现如下问题 RuntimeWarning: More than 20 figures have been opened. Figures created through the pyplot interface ( matplotlib.pyplot.figure ) are retained until explicitly closed and may consume too much memory. (To control this warning, see the rcParam figure.max_open_warning). fig, ax = plt.subplots(figsize=(10, 10)) 。

    2024年02月13日
    浏览(43)
  • LeetCode --- 1869. Longer Contiguous Segments of Ones than Zeros 解题报告

    Given a binary string  s , return  true  if the  longest  contiguous segment of  1 \\\' s is  strictly longer  than the  longest  contiguous segment of  0 \\\' s in  s , or return  false  otherwise . For example, in  s = \\\" 11 01 000 10\\\"  the longest continuous segment of  1 s has length  2 , and the longest continuous segment of  0 s has length 

    2024年02月15日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包