PSA: fix MultiDex build crashes

    PSA: fix MultiDex build crashes
    Cover image: “A Farmyard in Normandy” by Claude Monet — on WikiArt

    When life gives you lemons, you bump the heap size.

    [tl;dr is at the end of the article]

    One day, the moment comes. The moment when your builds fail with this error message:

    trouble writing output:
    Too many field references: 131000; max is 65536.
    You may try using --multi-dex option.

    What happened? Maybe the latest Play Services update did it. Or maybe it’s the sixth analytics SDK that the client has asked for in the app. Or something else. But one thing’s for sure: your app just joined the >65k methods club.

    This would have been a real issue just one year ago. There were some workarounds and some strategies to mitigate the issue, including some crazy dex splitting techniques, but none of them was always working, or something you’d want to do in the first place.


    MultiDex?

    Luckily enough, since Google has introduced MultiDex, things are awful simple to fix. Drop this in your build.gradle and you’re all set:

    android {
      // ...
      defaultConfig {
        // ...
        multiDexEnabled true
      }
    }

    No hacks (not in your code, anyway), no weird stuff. Clean and easy.

    Drawbacks

    Multidex has some drawbacks, unfortunately. First of all, it increases build times, which is never good. Secondarily, on Dalvik (but not on ART), it carries a startup time penalty as well while the classloader reads the second dex file from the apk.

    But the real penalty is that sometimes it might crash your build. Yes. The build. Not the app.

    UNEXPECTED TOP-LEVEL ERROR:
    java.lang.OutOfMemoryError: GC overhead limit exceeded
      at com.android.dx.cf.code.ExecutionStack.copy(ExecutionStack.java:66)
      at com.android.dx.cf.code.Frame.makeExceptionHandlerStartFrame(Frame.java:397)
      at com.android.dx.cf.code.Ropper.processBlock(Ropper.java:916)
      at com.android.dx.cf.code.Ropper.doit(Ropper.java:742)
      at com.android.dx.cf.code.Ropper.convert(Ropper.java:349)
      at com.android.dx.dex.cf.CfTranslator.processMethods(CfTranslator.java:280)
      at com.android.dx.dex.cf.CfTranslator.translate0(CfTranslator.java:137)
      at com.android.dx.dex.cf.CfTranslator.translate(CfTranslator.java:93)
      at com.android.dx.command.dexer.Main.processClass(Main.java:729)
      at com.android.dx.command.dexer.Main.processFileBytes(Main.java:673)
      at com.android.dx.command.dexer.Main.access$300(Main.java:83)
      at com.android.dx.command.dexer.Main$1.processFileBytes(Main.java:602)
      at com.android.dx.cf.direct.ClassPathOpener.processArchive(ClassPathOpener.java:284)
      at com.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:166)
      at com.android.dx.cf.direct.ClassPathOpener.process(ClassPathOpener.java:144)
      at com.android.dx.command.dexer.Main.processOne(Main.java:632)
      at com.android.dx.command.dexer.Main.processAllFiles(Main.java:505)
      at com.android.dx.command.dexer.Main.runMultiDex(Main.java:334)
      at com.android.dx.command.dexer.Main.run(Main.java:244)
      at com.android.dx.command.dexer.Main.main(Main.java:215)
      at com.android.dx.command.Main.main(Main.java:106)

    What this rather obscure error means is that the dexing phase of the build has basically ran out of memory.


    Fix the issue (tl;dr)

    Thanks to someone’s suggestion (I forgot who, I’m sorry, this happened a while ago), after spending quite some time scratching my head, I found out that there is a simple solution for this issue.

    Add this to your module’s build.gradle:

    android {
      // ...
      dexOptions {
        javaMaxHeapSize "2G"
      }
    }

    And voilà!

    Sebastiano Poggi

    Sebastiano Poggi

    “It depends” 🤷‍♂️ — Google Developer Expert for Android, Flutter and Identity. A geek 🤓 who has a serious thing for good design ✨ and for emojis 🤟working at JetBrains (opinions my own)

    London and elsewhere https://sebastiano.dev