Search code examples
androiddexsmali

Moving classes to multidex in existing APK


I am trying to move some frameworks from classes.dex to classes2.dex (MultiDex) from an existing APK. To do so I use smali. It seem it works for some apps and for some not:

D/dalvikvm( 1401): GC_CONCURRENT freed 258K, 24% free 13896K/18268K, paused 2ms+1ms, total 19ms
I/dalvikvm( 2737): Could not find method com.facebook.FacebookSdk.sdkInitialize, referenced from method com.foursquare.robin.App.onCreate
W/dalvikvm( 2737): VFY: unable to resolve static method 16788: Lcom/facebook/FacebookSdk;.sdkInitialize (Landroid/content/Context;)V
D/dalvikvm( 2737): VFY: replacing opcode 0x71 at 0x014d
E/dalvikvm( 2737): Could not find class 'com.twitter.sdk.android.core.u', referenced from method com.foursquare.common.app.support.am.a
W/dalvikvm( 2737): VFY: unable to resolve new-instance 8004 (Lcom/twitter/sdk/android/core/u;) in Lcom/foursquare/common/app/support/am;
D/dalvikvm( 2737): VFY: replacing opcode 0x22 at 0x0005
D/dalvikvm( 2737): DexOpt: unable to opt direct call 0xd553 at 0x09 in Lcom/foursquare/common/app/support/am;.a
I/MultiDex( 2737): VM with version 1.6.0 does not have multidex support
I/MultiDex( 2737): install
I/MultiDex( 2737): MultiDexExtractor.load(/data/app/com.foursquare.robin-1.apk, false)
I/MultiDex( 2737): Detected that extraction must be performed.
D/dalvikvm( 2737): GC_CONCURRENT freed 325K, 11% free 3389K/3784K, paused 1ms+0ms, total 5ms
I/MultiDex( 2737): Extraction is needed for file /data/data/com.foursquare.robin/code_cache/secondary-dexes/com.foursquare.robin-1.apk.classes2.zip
I/MultiDex( 2737): Extracting /data/data/com.foursquare.robin/code_cache/secondary-dexes/com.foursquare.robin-1.apk.classes-838392451.zip
I/MultiDex( 2737): Renaming to /data/data/com.foursquare.robin/code_cache/secondary-dexes/com.foursquare.robin-1.apk.classes2.zip
I/MultiDex( 2737): Extraction success - length /data/data/com.foursquare.robin/code_cache/secondary-dexes/com.foursquare.robin-1.apk.classes2.zip: 392940
I/MultiDex( 2737): load found 1 secondary dex files
D/dalvikvm( 2737): DexOpt: --- BEGIN 'com.foursquare.robin-1.apk.classes2.zip' (bootstrap=0) ---
I/dalvikvm( 2753): No library specified. The standard exception handler will be used
D/dalvikvm( 2753): DexOpt: load 18ms, verify+opt 57ms, 1428508 bytes
D/dalvikvm( 2737): DexOpt: --- END 'com.foursquare.robin-1.apk.classes2.zip' (success) ---
D/dalvikvm( 2737): DEX prep '/data/data/com.foursquare.robin/code_cache/secondary-dexes/com.foursquare.robin-1.apk.classes2.zip': unzip in 13ms, rewrite 232ms
I/MultiDex( 2737): install done
D/AndroidRuntime( 2737): Shutting down VM
W/dalvikvm( 2737): threadid=1: thread exiting with uncaught exception (group=0x42d5d140)
E/AndroidRuntime( 2737): FATAL EXCEPTION: main
E/AndroidRuntime( 2737): Process: com.foursquare.robin, PID: 2737
E/AndroidRuntime( 2737): java.lang.NoClassDefFoundError: com.twitter.sdk.android.core.u
E/AndroidRuntime( 2737):    at com.foursquare.common.app.support.am.a(SourceFile)
E/AndroidRuntime( 2737):    at com.foursquare.robin.App.onCreate(SourceFile:213)
E/AndroidRuntime( 2737):    at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1007)
E/AndroidRuntime( 2737):    at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4344)
E/AndroidRuntime( 2737):    at android.app.ActivityThread.access$1500(ActivityThread.java:135)
E/AndroidRuntime( 2737):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
E/AndroidRuntime( 2737):    at android.os.Handler.dispatchMessage(Handler.java:102)
E/AndroidRuntime( 2737):    at android.os.Looper.loop(Looper.java:136)
E/AndroidRuntime( 2737):    at android.app.ActivityThread.main(ActivityThread.java:5017)
E/AndroidRuntime( 2737):    at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime( 2737):    at java.lang.reflect.Method.invoke(Method.java:515)
E/AndroidRuntime( 2737):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
E/AndroidRuntime( 2737):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
E/AndroidRuntime( 2737):    at dalvik.system.NativeStart.main(Native Method)
W/ActivityManager( 1321):   Force finishing activity com.foursquare.robin/.activities.MainActivity

Is this because MultiDex is installed after Twitter API or do I have to modify something else than the dex files?


Solution

  • I'll first quote myself (from this blogpost):

    So what is main dex, and which classes it must include?

    On application start, the default ClassLoader has a single entry in its path - classes.dex file. It is also called the main dex. To support more than one dex file, the multidex support library implemented runtime patching of ClassLoader's path. This code should run as soon as you have the application context (the perfect place for it is in Application#attachBaseContext method).

    Therefore, the Application class should be definitely included in the main dex, since it should already be present when you patching the ClassLoader.

    Any other classes must be included? Yes. There's a thing called Dalvik verifier that has complex rules for determining inappropriate bytecode. For example, before loading the Application class, VM verifier performs its checks and if it finds a field whose type it can't resolve (yet), it will not allow to run instruction that accessing this field, even if at that point of time we already patched the ClassLoader and the class could be resolved.

    In your example, app's Application class (com.foursquare.robin.App) indirectly depends on com.twitter.sdk.android.core.u class. But as you can see in your logs, Dalvik verifier already patched the new-instance instruction:

    E/dalvikvm( 2737): Could not find class 'com.twitter.sdk.android.core.u', referenced from method com.foursquare.common.app.support.am.a
    W/dalvikvm( 2737): VFY: unable to resolve new-instance 8004 (Lcom/twitter/sdk/android/core/u;) in Lcom/foursquare/common/app/support/am;
    D/dalvikvm( 2737): VFY: replacing opcode 0x22 at 0x0005