Android Pie(9.0)中的多进程WebView

30
从Android Pie(API 28)开始,Google不允许在两个不同的进程中使用单个WebView实例。
文档链接:https://developer.android.com/reference/android/webkit/WebView.html#setDataDirectorySuffix(java.lang.String) 按要求,我调用了WebView.setDataDirectorySuffix("dir_name_no_separator"),但不幸的是,我收到了一个异常。我试图在第二个进程的Service onCreate()方法中调用此方法。
java.lang.RuntimeException: Unable to create service com.myapp.service.MyService: java.lang.IllegalStateException: Can't set data directory suffix: WebView already initialized
        at android.app.ActivityThread.handleCreateService(ActivityThread.java:3544)
        at android.app.ActivityThread.access$1300(ActivityThread.java:199)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1666)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
     Caused by: java.lang.IllegalStateException: Can't set data directory suffix: WebView already initialized
        at android.webkit.WebViewFactory.setDataDirectorySuffix(WebViewFactory.java:136)
        at android.webkit.WebView.setDataDirectorySuffix(WebView.java:2165)
        at com.myapp.service.MyService.onCreate(MyService.java:134)

我找不到这个异常的原因。我没有两次调用这个方法,也没有在我的主进程中调用它。有什么想法吗?

3个回答

30

问题已解决。

我的项目使用AdMob广告,我在Application类的onCreate()方法中调用了MobileAds.initialize()方法。广告初始化程序会加载一个WebView,但现在必须在调用WebView.setDataDirectorySuffix("dir_name_no_separator")方法之前在新进程中禁止这样做。

当创建第二个进程时,它也会通过相同的应用程序创建流程,这意味着它调用Application类中的相同onCreate()方法,该方法调用MobileAds.initialize()尝试创建一个新的WebView实例,由此导致崩溃。

IllegalStateException: Can't set data directory suffix: WebView already initialized

我是如何解决这个问题的?

我使用以下方法获取进程名称,并检查它是否是我的主要进程 - 调用MobileAds.initialize()方法,如果是我的第二个进程,则调用WebView.setDataDirectorySuffix("dir_name_no_separator")方法。

获取进程名称:

public static String getProcessName(Context context) {
    ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    for (ActivityManager.RunningAppProcessInfo processInfo : manager.getRunningAppProcesses()) {
        if (processInfo.pid == android.os.Process.myPid()) {
            return processInfo.processName;
        }
    }

    return null;
}

Application类的onCreate()方法:

if (!Utils.getProcessName(this).equals("YOUR_SECOND_PROCESS_NAME")) {
    MobileAds.initialize(this);
} else {
    WebView.setDataDirectorySuffix("dir_name_no_separator")
}

lluz,我遇到了类似的问题,但是异常不同,你知道这方面的任何信息吗?[链接](https://dev59.com/za7la4cB1Zd3GeqPXhx3?noredirect=1#comment91240321_52144354) - Mateen Chaudhry
8
这是一个小修改:如果 (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { String process = getProcessName(this); if (!getPackageName().equals(process)) WebView.setDataDirectorySuffix(process); } - Talu
5
从API 28开始,有一个新的API可以获取进程名称,它的性能更高。请查看https://developer.android.com/reference/android/app/Application.html#getProcessName()。 - Kiran Kumar
1
这里的“YOUR_SECOND_PROCESS_NAME”是什么? - Darshan
我已经弄清楚了,并在我的代码中使用了上面的代码,但现在Webview无法加载JavaScript,你有什么想法吗? - Darshan
显示剩余10条评论

5

总结包含所有改进的修复方案,这是 Kotlin 代码:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    if (packageName != Application.getProcessName()) {
        WebView.setDataDirectorySuffix(Application.getProcessName())
    }
}

将它添加到你的Application类的onCreate()方法中。

请注意,这只能解决最多2个进程的问题。如果你的应用程序使用更多进程,则必须为每个进程提供不同的WebView后缀。


“processName” 应该是什么? - android developer
应该是 "getProcessName()",我已经更新了答案。 - Micer
1
我明白了。您知道这个问题可能发生在哪些其他情况下吗?我尝试在合并的清单(创建APK后)中搜索“process”,但没有找到它。相反,我找到了两个提供程序的多进程条目,但我不认为这可能会导致问题,对吧? - android developer
@androiddeveloper 我现在不知道其他情况,但是在应用了这个修复后,我不再看到异常了。但如果你发现什么,请在这里分享。 :-) - Micer
我希望它能解决问题。我不知道为什么会出现这种情况。需要一些时间来决定它是否有所帮助。无论如何,谢谢。 - android developer

1
当广告导致错误时,应在应用程序类中处理。
try {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            val process = getProcessName()
            if (packageName != process) WebView.setDataDirectorySuffix(process)
        }

        MobileAds.initialize(this)
        AudienceNetworkAds.initialize(this)

    } catch (e: Error) {
        Timber.e(e)
    } catch (e: Exception) {
        Timber.e(e)
    }

现在这个问题还存在吗?如果不存在,是从哪个版本开始解决的? - android developer
在2020年,我添加了这段代码以消除崩溃,在此之后我没有检查过是否删除它。 - Abdur Rehman
你还记得吗,这是在 Android P 之后发生的吗? - android developer
是的,这个问题发生在P版本之后,当我们的应用程序从启动器图标和文件管理器中打开时,就会创建两个或更多实例! - Abdur Rehman
我明白了。你还记得出现这种情况的最高版本是多少吗?你怎么知道这么多细节?你自己复现过吗? - android developer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接