Multidex app can't find jar resource but only

2019-08-13 14:27发布

问题:

This is sort of an update to an older question I asked, I made it into a new question since I have a lot of new information and I never got any useful answers. I have sort of fixed the issue but I want to understand why.

The old questions:

java.util.MissingResourceException: Can't find bundle for base name javax.servlet.LocalStrings

Basically, a tiny percentage of my users will experience this crash.

Fatal Exception: java.lang.ExceptionInInitializerError
       at MyApp$2.run(MyApp.java:364)
       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
       at java.lang.Thread.run(Thread.java:818)
Caused by java.lang.NullPointerException: Attempt to invoke virtual method 'void java.util.jar.JarVerifier.removeMetaEntries()' on a null object reference
       at java.util.jar.JarFile.getInputStream(JarFile.java:381)
       at libcore.net.url.JarURLConnectionImpl.getInputStream(JarURLConnectionImpl.java:222)
       at java.net.URL.openStream(URL.java:470)
       at java.lang.ClassLoader.getResourceAsStream(ClassLoader.java:444)
       at java.util.ResourceBundle.handleGetBundle(ResourceBundle.java:516)
       at java.util.ResourceBundle.handleGetBundle(ResourceBundle.java:542)
       at java.util.ResourceBundle.handleGetBundle(ResourceBundle.java:542)
       at java.util.ResourceBundle.getBundle(ResourceBundle.java:228)
       at java.util.ResourceBundle.getBundle(ResourceBundle.java:139)
       at javax.servlet.GenericServlet.(GenericServlet.java)
       at MyApp$2.run(MyApp.java:364)
       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
       at java.lang.Thread.run(Thread.java:818)

This is happening when my app is trying to instantiate a Servlet. Basically GenericServlet is trying to load a resource, these are the lines it seems to be crashing on:

 private static final String LSTRING_FILE = "javax.servlet.LocalStrings";
 private static ResourceBundle lStrings =
        ResourceBundle.getBundle(LSTRING_FILE);

My app creates that Servlet as soon as it starts, basically on the onCreate of the Application class. So the fix for the crash I found, is ugly but it works. If I create the Servlet instance inside a thread launched from onCreate then it will crash for some users. If I instead create the Servlet on onCreate without delaying it, then it won't crash.

Basically this won't crash:

public void onCreate() {
    .....
    new MyServlet();
    .....

But this will:

public void onCreate() {
    .....
    new Thread(){..... new Servlet();}
    .....

So my guess is that multidex is making sure to keep the javax classes and resources on the main dex file when it sees the Servlet creation on onCreate. But as I mentioned on that previous question, I'm attempting to use multiDexKeepFile and more recently I've tried multiDexKeepProguard. So why aren't they working?

My latest attempt with multiDexKeepProguard in desperation was:

-keep class javax.servlet.http.HttpServlet
-keep class javax.servlet.GenericServlet
-keep class javax.servlet.*
-keep class javax.servlet.LocalStrings
-keepclassmembers class javax.servlet.LocalStrings

But it didn't help. The only thing that has helped is moving the first Servlet instantiation outside of the Thread and into onCreate. All subsequent Servlet creations and the start of the server happen on a Thread without issue.

So what am I doing wrong?

Thanks.