多进程中的 Android Pie (9.0) WebView

Posted

技术标签:

【中文标题】多进程中的 Android Pie (9.0) WebView【英文标题】:Android Pie (9.0) WebView in multi-process 【发布时间】:2019-01-21 10:28:12 【问题描述】:

android Pie (API 28) 开始,Google 不允许在 2 个不同的进程中使用单个 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)

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

【问题讨论】:

【参考方案1】:

总结所有改进的修复,这是 Kotlin 中的代码:

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

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

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

【讨论】:

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

当由于广告而出错时,则在应用程序类中

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)
    

【讨论】:

【参考方案3】:

解决了。

我的项目托管 AdMob 广告,我在 ApplicationonCreate() 中调用 MobileAds.initialize() 方法。广告初始化程序会加载一个 WebView,在您调用 WebView.setDataDirectorySuffix("dir_name_no_separator") 方法之前,现在禁止在新进程中执行此操作。

当创建第二个进程时,它也会经历相同的应用程序创建流程,这意味着它在Application 类中调用相同的onCreate(),该类调用尝试创建新WebViewMobileAds.initialize()实例并由此导致崩溃。

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;

应用类 onCreate():

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

【讨论】:

lluz 我面临着类似的问题,但有不同的例外,你知道吗? link 这是一个小修改: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) String process = getProcessName(this); if (!getPackageName().equals(process)) WebView.setDataDirectorySuffix(process); API 28 以后有一个新的 api 来获取进程名称,它的性能更高。查看developer.android.com/reference/android/app/… 这里的“YOUR_SECOND_PROCESS_NAME”是什么? @DarShan 您的第二个进程名称,正如您在 Manifest 中命名的那样,但您可以在上面的 cmets 中阅读 - 有一个更好的 API 可以获取进程名称,所以我建议改用它。

以上是关于多进程中的 Android Pie (9.0) WebView的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Android 9.0(PIE) 中获取 WIFI SSID?

FusedLocationProviderClient 在 Android Pie 9.0 中返回错误的纬度经度

Android Pie 9.0 将用户带到通知中心

下载管理器在 Android Pie 9.0 NetworkSecurityConfig 中不起作用:未指定网络安全配置,使用平台默认值

安卓9.0内测的背后,是上万App开发者半年来的适配优化

Android 9.0更新