Could you ever imagine a Unity Android game that used more than 64K Java methods? Neither could the architects of the Dalvik bytecode. Perhaps they did (I haven’t read the spec) and the blame falls on other elements in your toolchain. The long and short of it is if your game taps against the 64K method limit per DEX file you’re going to need to get down and dirty with your native plugins and/or build workflow. This post will attempt to walk you through the various ways to deal with it. First Things FirstThere’s no shortage of forum posts and blog entries on this subject. The most important takeaway is if you can manage to keep your game comfortably below this number, you will save yourself a lot of trouble. Know Your PluginsThe most common way to hit this limit in Unity is through the use of native plugins. Android native plugins are a necessity for almost all Unity games. Unfortunately, some plugins are quite large. Google Play Game Services, for example, is close to 25K methods on its own, a significant chunk of the 64K you are allotted. Super Brief Anatomy of Unity Android PluginsUnity Android Plugins typically consist of some C# Unity code along with native Android code and resources. The native code and resources will be packaged either as an Android Library Project or Android Archive (AAR) under the The classes in both Library projects and AARs exist in JAR files, which are simple zips of compiled Java class files. The AAR file is also simply a zip of various Android resources, some of which will be Steps to Minimize Method CountsThe only way to reduce the number of Java methods included in your game's APK using the standard Unity build system is to remove or modify the JAR files included with your Android native plugins. The alternative is to export your Unity Project as an Android Project where you can apply more advanced techniques. You should try each of the following techniques in order:
Most of this blog post will focus on the last bit because, at the time of writing, there aren’t a lot of resources that walk you through this approach. Exporting as an Android Project will be more disruptive to your development and build cycle. Until ProGuard or MultiDex are directly supported by Unity, you're best off going down this path as a last resort. What to Look for When TestingOnce you have your game under the 64K limit and are able to generate the APK again, the key thing to look for while testing your game is ProGuard and MultiDexProGuard is a tool used to obfuscate and trim unused classes and methods. MultiDex is technology that enables multiple DEX files within your APK, thus removing the 64K method limit for your game. Unity does not have direct support for either of these techniques, but you can make use of them if you export your project to an Android Project. When all else fails, ProGuard will hopefully bring you below the limit. If not, you can turn to MultiDex. MultiDex has the added strike of only working in API Level 14 (4.0) and up. It is natively supported in Android (5.0) and up. Support libraries must be used for 4.X. Finally, MultiDex comes with a set of Known Limitations. Exporting to an Android ProjectIf you need to use ProGuard or MultiDex, the first step is to export your Unity Project as an Android Project. Depending on the complexity of your Project, this in itself can be a daunting task. It also likely means no more Unity Cloud Build. When done correctly, however, it can work similarly to exporting to XCode for iOS. The Android Studio or Gradle project will need to be set up after the export, but this should be a one-time task. You will be able to re-export your project without having to set up the Android build configuration again. I’ve found three ways to successfully work with a project exported to Android. I’ll briefly cover the first two because they are simpler and may be preferable if your project is not too complex. The last approach requires a little more manual setup, but is probably the cleanest way to organize your project. It also may be your only option if you need MultiDex. A Few Words of CautionEven after exporting your game to Android Studio, it’s possible that the plugins your game uses depend on Unity Post Process scripts that will not translate to Android Studio or Gradle builds. You may still hit a dead end. Approach 1: Simple Unity Export/Android Studio ImportThis approach will work for games that do not use too many plugins. I imagine Unity and Android Studio will continue to improve this approach.
At this point, if all goes well, you should be able to run the project within Android Studio. Pros & Cons
Approach 2: Import the Exported Unity Project from SourceIn this approach you directly import the exported Unity Project into Android Studio from sources and then manually update the various modules and dependencies. The difference from the first approach is rather than importing The nice thing about this approach is that once you have the Android Studio project set up, you can re-export the Unity Project to the same location and in general will not need to update Android Studio Project. The downside to this approach is that your Android Project will be tied to Android Studio Project files. Configuring and managing the dependencies will be a challenge. Since I’d like to focus on the third approach, I’ll simply say once you have your project in Android Studio, it isn’t too difficult to enable ProGuard. However, the process of setting up the Android Studio Project involves correctly configuring each of the modules and dependencies using Android Studio’s UI. Depending on your familiarity with Android Studio project modules, this could be a tricky task. Further, I found getting MultiDex configured through the Android Studio UI challenging, which led me to the third approach. Approach 3: Configuring a Gradle Project for an Exported Unity ProjectGradle is the build tool Android settled into a few years back. Android Studio Projects may be synced with Gradle Projects. Though old Android Studio Project modules are still supported, new projects are based on Gradle files. In this approach we correctly set up the Gradle files for the exported Unity Project, at which point we can either work with them and build from Android Studio or from the command line. We are given access to useful Gradle tasks such as ProGuard and MultiDex. Set Up the Gradle WrapperIn the directory where you exported your game, set up the Gradle wrapper with the following command:
Gradle comes with Android Studio, so you should have some version of it installed. The above command will create a Create Root build.gradle fileIn the same directory, create your top level Gradle file
Create Your Application build.gradle FilePlace the following file in the main project sub-directory created for your Unity Project under your export directory (e.g.
Create Your settings.gradle FileBack in the root directory of your exported Android project, create a
At this point, if you had a super-simple Unity Project with no plugins, you should be good to go. Within Android Studio you may choose Open an existing Android Studio project. Navigate to and open the
You can see the complete list of Gradle build tasks with:
My Project Wasn’t That Simple :(Chances are if you are reading this, it’s because your project wasn’t that simple. When you export from Unity, in addition to a main app directory (e.g. Add the following file for each library project subdirectory created by the Unity export. Again, name this file(s)
Next, back in your
Finally, back in the
Resolving DependenciesIn some cases, you may have one library project that depends on another library project. For example, this output is shown because the
There is no hard and fast rule to interpret these dependencies, but in general, the name of the missing resource gives you a pretty good clue. For this case,
We see the resource is defined in
Now Gradle knows the Resolving Duplicate Class ConflictsWhen Unity exports the plugins as Library projects, it’s not unusual to see errors along these lines:
The
Since you’ll need to do this every time you export, you probably want to write script to clean up all the JARs after export. An alternative solution is to use the AAR, if one exists for the plugin, rather than the extracted Library Project Unity creates for the AAR when exporting. For this example, we find
Next we add a line to our root
Finally, add the dependency to
Other IssuesThere is a host of other issues you may or may not encounter converting your exported Unity Project to Gradle/Android Studio. In general, the two classes of issues include (1) conflicts between the The former will typically occur in regular Unity builds as well, during the manifest merging task. Resolving them requires tweaking the manifest entries. Usually, the errors tell you what the conflict is and provide some clue about how to resolve them. If possible, it’s better to resolve these in the main Unity Project, so you don’t need to re-perform the steps every time you export. The second issue regarding post process scripts is a lot more tricky, and may end up being a blocker for effectively working with the exported project. There are no general guidelines here. Resolving 64K DEX Method Limit in Your Gradle ProjectNow that we have our Unity Project in Gradle, we can use ProGuard to attempt to bring our method count below 64K, or we can enable MultiDex to support greater than 64K. Enabling ProGuardA whole separate blog post could be written on how to configure ProGuard for exported Unity Projects. Here we’ll show how to add ProGuard to your Gradle build script. Add the following to the
We specified two ProGuard configuration files – the standard one that’s included with the Android SDK ( To disable ProGuard, simply change the value of Enabling MultiDexTo enable MultiDex for your exported build, add the following lines to the
This will enable MultiDex support on Android 5.0 and up devices. To support Android 4.0 and up devices, you must make a few additional modifications. First, add a new dependency for the support library
Next, update the
If your Unity Project does not already have a main Complete Application build.gradle FileHere’s what the complete
This snippet also adds entries required to sign your app with a private key. The key password is drawn from an environment variable. If all is good, you can build your minified/multidexed game like so:
References
|