我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android如何解决WebView多进程崩溃的问题

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android如何解决WebView多进程崩溃的问题

小编给大家分享一下Android如何解决WebView多进程崩溃的问题,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

问题

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

WebView.setDataDirectorySuffix(suffix);

否则将会报出以下错误:

Using WebView from more than one process at once with the same data directory is not supported. https://crbug.com/5583771 com.android.webview.chromium.WebViewChromiumAwInit.startChromiumLocked(WebViewChromiumAwInit.java:63)2 com.android.webview.chromium.WebViewChromiumAwInitForP.startChromiumLocked(WebViewChromiumAwInitForP.java:3)3 com.android.webview.chromium.WebViewChromiumAwInit$3.run(WebViewChromiumAwInit.java:3)4 android.os.Handler.handleCallback(Handler.java:873)5 android.os.Handler.dispatchMessage(Handler.java:99)6 android.os.Looper.loop(Looper.java:220)7 android.app.ActivityThread.main(ActivityThread.java:7437)8 java.lang.reflect.Method.invoke(Native Method)9 com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:500)10 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:865)

通过使用官方提供的方法后问题只减少了一部分,从bugly后台依然能收到此问题的大量崩溃信息,以至于都冲上了崩溃问题Top3。

问题分析

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

public class WebViewChromiumAwInit { protected void startChromiumLocked() {   ...   AwBrowserProcess.start();   ...  }}public final class AwBrowserProcess { public static void start() {   ...   AwDataDirLock.lock(appContext);}

AwDataDirLock.java

abstract class AwDataDirLock { private static final String TAG = "AwDataDirLock"; private static final String EXCLUSIVE_LOCK_FILE = "webview_data.lock"; // This results in a maximum wait time of 1.5s private static final int LOCK_RETRIES = 16; private static final int LOCK_SLEEP_MS = 100; private static RandomAccessFile sLockFile; private static FileLock sExclusiveFileLock; static void lock(final Context appContext) {  try (ScopedSysTraceEvent e1 = ScopedSysTraceEvent.scoped("AwDataDirLock.lock");    StrictModeContext ignored = StrictModeContext.allowDiskWrites()) {   if (sExclusiveFileLock != null) {    // We have already called lock() and successfully acquired the lock in this process.    // This shouldn't happen, but is likely to be the result of an app catching an    // exception thrown during initialization and discarding it, causing us to later    // attempt to initialize WebView again. There's no real advantage to failing the    // locking code when this happens; we may as well count this as the lock being    // acquired and let init continue (though the app may experience other problems    // later).    return;   }   // If we already called lock() but didn't succeed in getting the lock, it's possible the   // app caught the exception and tried again later. As above, there's no real advantage   // to failing here, so only open the lock file if we didn't already open it before.   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) {    // Failing to create the lock file is always fatal; even if multiple processes    // are using the same data directory we should always be able to access the file    // itself.     throw new RuntimeException("Failed to create lock file " + lockFile, e);    }   }   // Android versions before 11 have edge cases where a new instance of an app process can   // be started while an existing one is still in the process of being killed. This can   // still happen on Android 11+ because the platform has a timeout for waiting, but it's   // much less likely. Retry the lock a few times to give the old process time to fully go   // away.   for (int attempts = 1; attempts <= LOCK_RETRIES; ++attempts) {    try {     sExclusiveFileLock = sLockFile.getChannel().tryLock();    } catch (IOException e) {    // Older versions of Android incorrectly throw IOException when the flock()    // call fails with EAGAIN, instead of returning null. Just ignore it.    }    if (sExclusiveFileLock != null) {     // We got the lock; write out info for debugging.     writeCurrentProcessInfo(sLockFile);     return;    }    // If we're not out of retries, sleep and try again.    if (attempts == LOCK_RETRIES) break;    try {     Thread.sleep(LOCK_SLEEP_MS);    } catch (InterruptedException e) {    }   }   // We failed to get the lock even after retrying.   // Many existing apps rely on this even though it's known to be unsafe.   // Make it fatal when on P for apps that target P or higher   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 {    Log.w(TAG, error);   }  } } private static void writeCurrentProcessInfo(final RandomAccessFile file) {  try {   // Truncate the file first to get rid of old data.   file.setLength(0);   file.writeInt(Process.myPid());   file.writeUTF(ContextUtils.getProcessName());  } catch (IOException e) {   // Don't crash just because something failed here, as it's only for debugging.   Log.w(TAG, "Failed to write info to lock file", e);  } } private static String getLockFailureReason(final RandomAccessFile file) {  final StringBuilder error = new StringBuilder("Using WebView from more than one process at "    + "once with the same data directory is not supported. https://crbug.com/558377 "    + ": Current process ");  error.append(ContextUtils.getProcessName());  error.append(" (pid ").append(Process.myPid()).append("), lock owner ");  try {   int pid = file.readInt();   String processName = file.readUTF();   error.append(processName).append(" (pid ").append(pid).append(")");   // Check the status of the pid holding the lock by sending it a null signal.   // This doesn't actually send a signal, just runs the kernel access checks.   try {    Os.kill(pid, 0);    // No exception means the process exists and has the same uid as us, so is    // probably an instance of the same app. Leave the message alone.   } catch (ErrnoException e) {    if (e.errno == OsConstants.Eclass="lazy" data-srcH) {     // pid did not exist - the lock should have been released by the kernel,     // so this process info is probably wrong.     error.append(" doesn't exist!");    } else if (e.errno == OsConstants.EPERM) {     // pid existed but didn't have the same uid as us.     // Most likely the pid has just been recycled for a new process     error.append(" pid has been reused!");    } else {     // EINVAL is the only other documented return value for kill(2) and should never     // happen for signal 0, so just complain generally.     error.append(" status unknown!");    }   }  } catch (IOException e) {   // We'll get IOException if we failed to read the pid and process name; e.g. if the   // lockfile is from an old version of WebView or an IO error occurred somewhere.   error.append(" unknown");  }  return error.toString(); }}

lock方法会对webview数据目录中的webview_data.lock文件在for循环中尝试加锁16次,注释中也说明了这么做的原因:可能出现的极端情况是一个旧进程正在被杀死时一个新的进程启动了,看来Google工程师对这个问题也很头痛;如果加锁成功会将该进程id和进程名写入到文件,如果加锁失败则会抛出异常。所以在android9.0以上检测应用是否存在多进程共用WebView数据目录的原理就是进程持有WebView数据目录中的webview_data.lock文件的锁。所以如果子进程也对相同文件尝试加锁则会导致应用崩溃。

解决方案

目前大部分手机会在应用崩溃时自动重启应用,猜测当手机系统运行较慢时这时就会出现注释中提到的当一个旧进程正在被杀死时一个新的进程启动了的情况。既然获取文件锁失败就会发生崩溃,并且该文件只是用于加锁判断是否存在多进程共用WebView数据目录,每次加锁成功都会重新写入对应进程信息,那么我们可以在应用启动时对该文件尝试加锁,如果加锁失败就删除该文件并重新创建,加锁成功就立即释放锁,这样当系统尝试加锁时理论上是可以加锁成功的,也就避免了这个问题的发生。

private static void handleWebviewDir(Context context) {  if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {   return;  }  try {   String suffix = "";   String processName = getProcessName(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();  } }

使用此方案应用上线后该问题崩溃次数减少了90%以上。也许Google工程师应该考虑下换一种技术方案检测应用是否存在多进程共用WebView数据目录。

以上是“Android如何解决WebView多进程崩溃的问题”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注编程网行业资讯频道!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android如何解决WebView多进程崩溃的问题

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android如何解决WebView多进程崩溃的问题

小编给大家分享一下Android如何解决WebView多进程崩溃的问题,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!问题在android 9.0系统上如果多个进程
2023-06-14

如何解决win10系统崩溃问题

这篇文章主要介绍了如何解决win10系统崩溃问题,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。1.首先,先重启电脑2.然后,找到启动修复的按钮,然后对系统进行修复。3.然后对
2023-06-27

如何解决使Eclipse崩溃的JVM terminated问题

这期内容当中小编将会给大家带来有关如何解决使Eclipse崩溃的JVM terminated问题,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。本文将介绍如何解决JVM terminated令Eclipse
2023-06-17

如何解决腾讯云服务器崩溃问题

1.确认服务器是否真的崩溃在处理服务器崩溃问题之前,首先需要确认服务器是否真的崩溃了。可以通过以下几种方式来确认:尝试通过SSH连接服务器,如果连接失败,则可能是服务器崩溃了。查看服务器的监控数据,如果CPU使用率、内存使用率等数据都为0,则可能是服务器崩溃了。尝试访问服务器上运行的应用程序,如果无法访问,则可能是服务器崩溃了。2.重启服务器如果确认服务器确实崩溃了,那么第一步是
2023-10-26

如何解决chm在WIN7下崩溃生成chw文件的问题

安装有Visual Studwww.cppcns.comio的机器,可以调试一下,看到调用栈到了hhctrl.ocx控件处出问题了。 以下按网上的办法解决此问题,打开cmd窗口,按照下面次序http://www.cppcns.com在运行:
2023-05-25

php如何解决进程太多的问题

小编给大家分享一下php如何解决进程太多的问题,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!php进程太多的解决办法:1、通过“ps -ef | wc -l”命
2023-06-25

如何解决Android中WebView的input上传照片兼容问题

小编给大家分享一下如何解决Android中WebView的input上传照片兼容问题,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!问题前几天接到的一个需求,是关于
2023-05-30

记一次线程爆满导致服务器崩溃的问题排查及解决

这篇文章主要介绍了记一次线程爆满导致服务器崩溃的问题排查及解决,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2022-11-13

win8下火狐安装firebug调试程序后打开崩溃的问题解决方法

自编程从装了win8以来,不是蓝屏就是死机,然后就是触摸板不能用,再又是USB接口突然不灵了。如果说之前的种种问题还是可以可以解决的吧,今天调试程序,要装个火狐的(主要是用firebug)调试网站,装好了刚打开就提示编程客栈崩溃,以为是火狐
2023-06-04

如何解决E4A在XP系统下出现内存不能为read崩溃情况的问题

  方法一:1:我的电脑右键——属性编程2:点击高级——性能设置3:点击数javascript据执行保护——为除下列选定程序....——添加E
2023-06-03

如何解决Java进程不见了的问题

本篇内容介绍了“如何解决Java进程不见了的问题”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1. 被操作系统审判了以下问题已经不止一个小伙
2023-06-15

Nodejs中解决cluster模块的多进程如何共享数据问题

前述nodejs在v0.6.x之后增加了一个模块cluster用于实现多进程,利用child_process模块来创建和管理进程,增加程序在多核CPU机器上的性能表现。本文将介绍利用cluster模块创建的多线程如何共享数据的问题。进程间数
2022-06-04

java多线程并发问题如何解决

在Java中,可以使用以下方法来解决多线程并发问题:1. 使用synchronized关键字:可以通过在方法或代码块前加上synchronized关键字来实现同步,确保同一时间只有一个线程可以访问被同步的代码块或方法。2. 使用Lock接口
2023-09-27

如何解决Android中webview旋转屏幕导致页面重新加载问题

这篇文章给大家分享的是有关如何解决Android中webview旋转屏幕导致页面重新加载问题的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。Android webview旋转屏幕导致页面重新加载问题解决办法1. 在
2023-05-30

如何解决PHP无法实现多线程的问题

这篇文章主要为大家展示了“如何解决PHP无法实现多线程的问题”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何解决PHP无法实现多线程的问题”这篇文章吧。其实的是大多数情况下,你大可不必使用fo
2023-06-17

多进程编程中遇到的Python问题及解决方法

多进程编程中遇到的Python问题及解决方法,需要具体代码示例在Python中,多进程编程是一种常用的并发编程方式。它可以有效利用多核处理器的优势,提高程序的运行效率。然而,在进行多进程编程时,我们也会遇到一些问题。本文将介绍几个常见的问题
2023-10-22

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录