将 类 移动到现有 APK 中的 multidex

Moving classes to multidex in existing APK

我正在尝试将现有 APK 中的某些框架从 classes.dex 移动到 classes2.dex (MultiDex)。为此,我使用 smali。它似乎适用于某些应用程序,但对某些应用程序无效:

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.access00(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

这是因为 MultiDex 是在 Twitter API 之后安装的,还是我必须修改除 dex 文件以外的其他内容?

我先引用我自己的话(来自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.

在您的示例中,应用程序的应用程序 class (com.foursquare.robin.App) 间接依赖于 com.twitter.sdk.android.core.u class。但是正如您在日志中看到的那样,Dalvik 验证器已经修补了新实例指令:

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