Overcoming Android’s 64K Method Limit with Multidex: Real‑World Strategies
This article examines the 64K method and linear memory constraints in large Android applications, compares the official multidex support library with custom dex‑splitting approaches used by WeChat, QQ, and Facebook, and proposes a testing‑based loading scheme that minimizes startup latency while keeping the primary dex lightweight.
Android Official Solution
Android provides an official multidex support library . Enabling it is as simple as setting multiDexEnabled true in the defaultConfig block, which causes the build tools to split the application into multiple dex files named classesN.dex placed at the root of the APK.
defaultConfig {
...
// Enabling multidex support.
multiDexEnabled true
}The split dex files are merged into a single OAT file during the ART compilation phase on Android 5.0+, reducing startup time. The main dex must contain classes referenced directly by the application entry points (e.g., Application, Activity, ContentProvider, Service), which is controlled by the mainDexClasses script available from SDK build tools version 21 onward.
mainDexClasses [--output <output file>] <application path>The script analyzes compiled classes and generates a list of classes that must reside in the primary dex. It works in three steps: environment checks, ProGuard‑based shrinking using mainDexClasses.rules, and building the final list with MainDexListBuilder.
Example of direct and indirect reference classes:
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
DirectReferenceClass test = new DirectReferenceClass();
}
}
public class DirectReferenceClass {
public DirectReferenceClass() {
InDirectReferenceClass test = new InDirectReferenceClass();
}
}
public class InDirectReferenceClass {
public InDirectReferenceClass() {
}
}For devices below Android 5.0, additional dex files must be loaded manually, typically in attachBaseContext:
public class HelloMultiDexApplication extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
}If a class required at runtime is not in the primary dex, a NoClassDefFoundError can occur. The fix is to add the missing class to the --main-dex-list parameter:
afterEvaluate {
tasks.matching { it.name.startsWith('dex') }.each { dx ->
if (dx.additionalParameters == null) {
dx.additionalParameters = []
}
dx.additionalParameters += '--multi-dex'
dx.additionalParameters += "--main-dex-list=$projectDir/<filename>"
}
}This approach is simple and requires few dependencies, but large secondary dex files can cause noticeable black screens or ANR during the first load.
WeChat/QQ Loading Solution
WeChat (111,052 methods) and QQ split the app into one primary dex and two secondary dex files stored under assets/secondary-program-dex-jars/secondary-N.dex.jar. The secondary dexes are loaded in a background thread after verifying MD5 checksums to prevent tampering.
/**
* Makes an array of dex/resource path elements, one per element of
* the given array.
*/
private static Element[] makeDexElements(ArrayList<File> files, File optimizedDirectory,
ArrayList<IOException> suppressedExceptions) {
// implementation omitted
}The packaging rule requires all Application, ContentProvider, and exported Activity / Service / Receiver classes and their indirect dependencies to reside in the primary dex, amounting to roughly 41,306 methods for WeChat.
public MainDexListBuilder(String rootJar, String pathString) throws IOException {
path = new Path(pathString);
ClassReferenceListBuilder mainListBuilder = new ClassReferenceListBuilder(path);
}Loading logic checks whether the dex files have been optimized; if not, they are loaded in a separate thread. The approach yields good user experience but is complex and may eventually hit the method limit as the indirect dependency set grows.
Facebook Loading Solution
Facebook uses a lightweight nodex process to host secondary dex files. Only a minimal set of classes (the application and a dedicated NodexSplashActivity) are required in the primary dex.
<activity android:exported="false" android:process=":nodex"
android:name="com.facebook.nodex.startup.splashscreen.NodexSplashActivity">The nodex process is started first; if the dex is already initialized, it immediately launches the main UI. This reduces startup latency but adds ~100 ms overhead for the extra process.
Test Loading Solution
A proposed test‑based scheme keeps the primary dex minimal, similar to Facebook, while loading secondary dexes directly in the main process without spawning an extra process. The steps are:
Use the same asset‑based dex format as WeChat.
Ensure the primary dex contains only classes needed for dex loading.
During attachBaseContext, if dex is not initialized, pause the main process, start a helper process to load dex, then resume.
Process synchronization can be achieved via a temporary file that the loader creates and deletes; the main process polls the file every 100 ms. Tests show this does not trigger ANR because the main process is not in the foreground during the wait.
Summary
After exploring dex and ART details, the following actions are planned for WeChat:
Adopt the classesN.dex naming convention.
Validate the feasibility of the fourth loading scheme, handling cases where a Service launches the UI.
Continue testing and refining the approach.
The author welcomes feedback and discussion on the presented solutions.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
WeChat Client Technology Team
Official account of the WeChat mobile client development team, sharing development experience, cutting‑edge tech, and little‑known stories across Android, iOS, macOS, Windows Phone, and Windows.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
