30

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

Documentation: https://developer.android.com/reference/android/webkit/WebView.html#setDataDirectorySuffix(java.lang.String)

As required, I called WebView.setDataDirectorySuffix("dir_name_no_separator") but unfortunately, I get an exception. I tried to call this method inside the 2nd process 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)

I couldn't find any reason for that exception. I didn't call this method twice nor I called it in my main process. Any ideas?

Lior Iluz
  • 26,213
  • 16
  • 65
  • 114

3 Answers3

30

Solved.

My project hosts AdMob ads and I call the MobileAds.initialize() method inside my Application class onCreate(). The ads initializer loads a WebView which is now forbidden to do in a new process before you call the WebView.setDataDirectorySuffix("dir_name_no_separator") method.

When the second process is created, it also goes through the same application create flow, meaning it calls the same onCreate() inside the Application class, which calls the MobileAds.initialize() that tries to create a new WebView instance and by that causes the crash.

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

How I solved this?

I get the process name using the below method and check if it's my main process - call the MobileAds.initialize() method and if it's my second process, call the WebView.setDataDirectorySuffix("dir_name_no_separator") method.

Get process name:

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 class onCreate():

if (!Utils.getProcessName(this).equals("YOUR_SECOND_PROCESS_NAME")) {
    MobileAds.initialize(this);
} else {
    WebView.setDataDirectorySuffix("dir_name_no_separator")
}
yoAlex5
  • 29,217
  • 8
  • 193
  • 205
Lior Iluz
  • 26,213
  • 16
  • 65
  • 114
  • lluz i am facing similar problem but with different exception do you know anything about it? [link](https://stackoverflow.com/questions/52144354/admob-banner-ad-not-loading-in-android-p?noredirect=1#comment91240321_52144354) – Mateen Chaudhry Sep 08 '18 at 06:41
  • 8
    This is a little modification: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { String process = getProcessName(this); if (!getPackageName().equals(process)) WebView.setDataDirectorySuffix(process); } – Talu Jun 01 '19 at 00:12
  • 5
    API 28 onwards there is a new api to get process name which is more performant. Check out https://developer.android.com/reference/android/app/Application.html#getProcessName() – Kiran Kumar Jul 18 '19 at 05:54
  • 1
    What is "YOUR_SECOND_PROCESS_NAME" here? – Darshan May 16 '20 at 12:50
  • @DarShan your second process name, as you named it on the Manifest but as you can read in comments above - there's a better API to get the process name so I advise using it instead. – Lior Iluz May 16 '20 at 16:05
  • I figured that out and used the above code in my code, however now the webview can’t load the javascript, you have any idea on that?? – Darshan May 16 '20 at 16:07
  • What if I don't have "process" anywhere in the manifest, and yet I see this issue? I've tried searching "process" in the merged manifest (after creating the APK), and didn't find it. Instead I've found 2 entries with `multiprocess` for 2 providers, but I don't think this could cause it, right? – android developer Oct 12 '20 at 08:56
  • @androiddeveloper don't think so. Do you (or any SDK you use) instantiate different WebViews on your application class? Check if you do that more than once on runtime. – Lior Iluz Oct 12 '20 at 11:42
  • @LiorIluz Might be. There are normal ones, and there are of ads. There might be more too. Why does it matter? – android developer Oct 12 '20 at 12:48
  • @androiddeveloper did you try adding my code and see if it helps? Only add if (myProcess) { WebView.setDataDirectorySuffix() } . no else. {} – Lior Iluz Oct 13 '20 at 12:07
  • I don't understand. I added the next code, but the app isn't published yet: `val processName = Application.getProcessName(); if (context.packageName != processName) WebView.setDataDirectorySuffix(processName)` . Sorry for the formatting here. Hope you can read what's written. – android developer Oct 13 '20 at 12:34
  • @androiddeveloper why do you compare package to process name? You should compare your process name... just in case your app has another process that is being initialised somehow. I'm not saying it is but this is the only reason I know this crash can occur and a simple if condition can solve. – Lior Iluz Oct 14 '20 at 07:42
  • The package name is the default name of the process, unless you've changed it in manifest, no? – android developer Oct 14 '20 at 07:45
  • @androiddeveloper not sure, don't remember actually, did it a while ago :) If it is and you make sure to call this method only on your process and it's still crashing, I have no idea why unfortunately. – Lior Iluz Oct 14 '20 at 11:04
5

To summarize the fix with all the improvements, this is the code in Kotlin:

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

Add it to your Application class to onCreate() method.

Note this is will only fix problem with maximum 2 processes. If your app is using more, you have to provide different WebView suffix for each of them.

Micer
  • 8,731
  • 3
  • 79
  • 73
  • What should be "processName" ? – android developer Oct 12 '20 at 09:01
  • should be "getProcessName()", I've updated the answer – Micer Oct 13 '20 at 08:37
  • 1
    I see. Do you know perhaps of other cases this issue can occur ? I've tried searching "process" in the merged manifest (after creating the APK), and didn't find it. Instead I've found 2 entries with multiprocess for 2 providers, but I don't think this could cause it, right? – android developer Oct 13 '20 at 10:29
  • @androiddeveloper I don't know about any other case right now, after applying this fix I don't see the exception anymore. But if you find something, share it here pls. :-) – Micer Oct 14 '20 at 08:04
  • I hope it will fix it. I have no idea why it occurs. Will take some time to decide if it helps or not. Anyway thanks – android developer Oct 14 '20 at 08:25
1

when error due to ads, then in application class

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)
    }
Abdur Rehman
  • 1,247
  • 10
  • 13