当前位置:   article > 正文

Android 的65535放法数超限问题解决方案-AS方式、apk解析合并多dex、smali文件问题_caused by: com.android.tools.r8.utils.b: cannot fi

caused by: com.android.tools.r8.utils.b: cannot fit requested classes in a s
  • 我们在开发android应用的时候,如果依赖了很多三方库,应该会遇到65535放法数超限的问题,比如使用Android Studio进行构建apk的时候失败了,并报出如下错误日志
    1. AGPBI: {"kind":"error","text":"Cannot fit requested classes in a single dex file (# methods: 102169 > 65536)","sources":[{}],"tool":"D8"}
    2. com.android.builder.dexing.DexArchiveMergerException: Error while merging dex archives:
    3. The number of method references in a .dex file cannot exceed 64K.
    4. Learn how to resolve this issue at https://developer.android.com/tools/building/multidex.html
    5. at com.android.builder.dexing.D8DexArchiveMerger.getExceptionToRethrow(D8DexArchiveMerger.java:132)
    6. at com.android.builder.dexing.D8DexArchiveMerger.mergeDexArchives(D8DexArchiveMerger.java:119)
    7. at com.android.build.gradle.internal.transforms.DexMergerTransformCallable.call(DexMergerTransformCallable.java:102)
    8. at com.android.build.gradle.internal.tasks.DexMergingTaskRunnable.run(DexMergingTask.kt:445)
    9. at com.android.build.gradle.internal.tasks.Workers$ActionFacade.run(Workers.kt:348)
    10. at org.gradle.workers.internal.AdapterWorkAction.execute(AdapterWorkAction.java:50)
    11. at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:47)
    12. at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1$1.create(NoIsolationWorkerFactory.java:65)
    13. at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1$1.create(NoIsolationWorkerFactory.java:61)
    14. at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:98)
    15. at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.execute(NoIsolationWorkerFactory.java:61)
    16. at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
    17. at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
    18. at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:416)
    19. at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:406)
    20. at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
    21. at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
    22. at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
    23. Cannot fit requested classes in a single dex file (# methods: 102169 > 65536)
    24. at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:102)
    25. at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36)
    26. at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
    27. at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:56)
    28. at org.gradle.workers.internal.DefaultWorkerExecutor$3.call(DefaultWorkerExecutor.java:215)
    29. at org.gradle.workers.internal.DefaultWorkerExecutor$3.call(DefaultWorkerExecutor.java:210)
    30. at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    31. at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:215)
    32. at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
    33. at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:131)
    34. at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    35. at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    36. at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
    37. at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
    38. at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    39. at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    40. at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
    41. at java.lang.Thread.run(Thread.java:748)
    42. Caused by: com.android.tools.r8.CompilationFailedException: Compilation failed to complete
    43. at com.android.tools.r8.utils.O.a(:65)
    44. at com.android.tools.r8.D8.run(:11)
    45. at com.android.builder.dexing.D8DexArchiveMerger.mergeDexArchives(D8DexArchiveMerger.java:117)
    46. Caused by: com.android.tools.r8.CompilationFailedException: Compilation failed to complete
    47. ... 34 more
    48. Caused by: com.android.tools.r8.utils.b: Error: null, Cannot fit requested classes in a single dex file (# methods: 102169 > 65536)
    49. at com.android.tools.r8.utils.y0.a(:21)
    50. at com.android.tools.r8.dex.K.a(:56)
    51. at com.android.tools.r8.dex.K$h.a(:5)
    52. at com.android.tools.r8.dex.b.b(:15)
    53. at com.android.tools.r8.dex.b.a(:38)
    54. Caused by: com.android.tools.r8.utils.b: Error: null, Cannot fit requested classes in a single dex file (# methods: 102169 > 65536)
    55. at com.android.tools.r8.D8.d(:87)
    56. at com.android.tools.r8.D8.b(:1)
    57. at com.android.tools.r8.utils.O.a(:30)
    58. ... 36 more
    59. Execution failed for task ':app:mergeDexDebug'.
    60. > A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade
    61. > com.android.builder.dexing.DexArchiveMergerException: Error while merging dex archives:
    62. The number of method references in a .dex file cannot exceed 64K.
    63. Learn how to resolve this issue at https://developer.android.com/tools/building/multidex.html
    64. * Try:
    65. Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
  • 这个问题很好解决的,只需要在build.gradle中配置支持多dex参数
    1. android {
    2. ...
    3. defaultConfig {
    4. ...
    5. multiDexEnabled true//解决方法数超65536限制问题
    6. }
    7. }
  • 然后再自己的Application里重写attachBaseContext(),加入MultiDex.install(context)即可
    1. ...
    2. import androidx.multidex.MultiDex;
    3. public class MyApplication extends Application {
    4. @Override
    5. protected void attachBaseContext(Context context) {
    6. MultiDex.install(context);
    7. super.attachBaseContext(context);
    8. }
    9. }
  • 然后将自己的MyApplication注册到AndroidManifest.xml中
    1. <?xml version="1.0" encoding="utf-8"?>
    2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    3. xmlns:tools="http://schemas.android.com/tools"
    4. package="com.xx.xx">
    5. <application
    6. android:name="com.xx.xx.MyApplication"
    7. >
    8. </application>
    9. </manifest>
  • 通过Android Studio处理65535放法数超限的问题是很好解决的,如果我们是逆向开发,比如有一个游戏应用接入了我们的SDK,给了一个apk给我们,我们需要拿到这个apk替换掉我们SDK的代码,并加入一些其他的插件SDK代码,我们应该怎么办呢
  • 首先接入我们SDK的应用不管他们出包的时候是否有放法数超限的问题,都进行上述处理65535放法数超限的解决方案去处理,这样处理过后,当我们合并到apk中的代码如果超限制了,也不用在合并的时候在Application里面去加入 MultiDex.install(context)了,没有超放法数也不会有什么影响
  • 合并代码的时候需要将母包通过apktool进行解析,然后将旧的代码或资源删除,然后加入新的代码和资源,加入新的代码就会引入65535放法数超限的问题,因为一个dex文件最多只能有65535个方法,多了的话,运行时会奔溃,合并的策略有多种,下面我们来具体说下吧
  • 方案一:直接合并到第一个smali文件夹中,然后对这个smali文件中的smali代码文件进行遍历计算它的方法数,当即将超过限制的放法数(最大方法数可以自己定义大小,不一定是65535,最好方法数不要太满了)后,就进行分包,比如分了一个smali_classes2,但是如何计算samli中的方法数呢,这个就需要去研究下smali的语法了,smali类里面的方法都是.method开头的,可以计算.method的个数来确定一个smali文件的方法数
  • 方案二:将需要合并的SDK代码文件,在生成smali文件的时候,就将其方法数算出来,比如我们合并一个apk、aar、jar等可以通过第三方开源dex-method-counts工具计算得到里面的方法数,也可以使用Android SDK自带的dexdump工具执行命令dexdump -f classes.dex | findstr method_ids_size 得到,dexdump工具位于sdk的build-tools/28.0.3下。得到方法数后就保存起来,下次合并的时候就不用再计算了,接着我们遍历解析后母包的samli目录,如果只有smali一个文件,就创建一个smali_classes2目录,然后将SDK的smali合并进去,并记录当前的方法数,这样一个一个进行合并,直到下一个SDK的放法数+待合并的smali目录放法数大于限制数,再创建一个smali_classesX,这样循环合并下去,保证新增的smali目录不超限制,这样有个缺点就是某些smali目录可能放法数不是很多,母包自带的第一个也没有利用起来,但是这个合并的效率很高,不用遍历每个smali代码文件去计算方法数,有点简单粗暴
    1. H:\Sdk\build-tools\28.0.3>dexdump -f classes.dex | findstr method_ids_size
    2. method_ids_size : 1388
  • 虽然我们合并代码的多dex问题解决了,但是还有一个问题就是,我们在运行的时候可能会出现找不到类的异常ClassNotFindException,程序一运行就奔溃,上面我们不是已经配置了MultiDex.install(context);吗,原因很简单,就是我们的MyApplication 也许继承的是我们SDK的Application,而我们的Application也可能会集成其他的Application,而合并SDK里面的代码可能不在第一个dex里面,这时候我们就需要将MyApplication继承的父类,或者父类的父类找到,然后合并到第一个dex中去
  • 首先我们需要解析AndroidManifest.xml 文件得到里面配置的Application类路径,然后定位到smali代码文件中去,然后去查找其父类,如果父类不在第一个smali目录中则移动到一个smali目录中,接着再看移动的这个Application类的父类,一直寻找、移动下去,直到父类是java/lang/Object,就结束了。这样就能保证运行是不会报ClassNotFindException了
  • 对于逆向开发的多dex超65535放法数问题,我们只是讨论了一些可行的方案,大家可以自己手动去实现,也许还有更好的方法

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/244322
推荐阅读
相关标签
  

闽ICP备14008679号