Android中每个dex文件的方法数量限制为64K

8

我遇到了这个问题java.lang.IllegalArgumentException: method ID not in [0, 0xffff]: 65536,我决定从dex文件中排除一些方法。我的gradle.build文件如下:

compile ('com.google.android.gms:play-services:+') {
        exclude group: "com.google.android.gms.analytics"
        exclude group: "com.google.android.gms.games"
        exclude group: "com.google.android.gms.plus"
        exclude group: "com.google.android.gms.drive"
        exclude group: "com.google.android.gms.ads"
    }

我认为这段代码有误,因为出现了错误method ID not in [0, 0xffff]...。我该如何排除Google Play服务中不必要的部分?我只使用地图和GCM。
更新:
感谢reVerse。这是非常有用的代码。还有一个脚本可以获取方法数(也可以查看现有包的名称)https://gist.github.com/JakeWharton/6002797source ./dex.sh; dex-method-count-by-package test.apk
在使用来自reVerse答案的代码片段之前。
Count of methods / Package
...
22484   com.google.android.gms
2   com.google.android.gms.actions
578 com.google.android.gms.ads
152 com.google.android.gms.ads.doubleclick
25  com.google.android.gms.ads.identifier
86  com.google.android.gms.ads.internal
86  com.google.android.gms.ads.internal.rawhtmlad
86  com.google.android.gms.ads.internal.rawhtmlad.client
88  com.google.android.gms.ads.mediation
4   com.google.android.gms.ads.mediation.admob
73  com.google.android.gms.ads.mediation.customevent
26  com.google.android.gms.ads.purchase
118 com.google.android.gms.ads.search
...
858 com.google.android.gms.games.internal.api
43  com.google.android.gms.games.internal.constants
8   com.google.android.gms.games.internal.data
31  com.google.android.gms.games.internal.events
9   com.google.android.gms.games.internal.experience
215 com.google.android.gms.games.internal.game
56  com.google.android.gms.games.internal.multiplayer
23  com.google.android.gms.games.internal.notification
80  com.google.android.gms.games.internal.player
86  com.google.android.gms.games.internal.request
...

使用reVerse的代码片段后,广告、游戏等包被删除了。

1个回答

9

更新 - Google Play服务 6.5 (12-08-14)

Google在版本6.5中终于解除了Google Play服务的捆绑。因此,从现在开始,您可以有选择地将API编译到可执行文件中。

示例(仅使用AdMob和Android Wear API)

compile 'com.google.android.gms:play-services-wearable:6.5.+'
compile 'com.google.android.gms:play-services-ads:6.5.+'

如需了解其他Google Play服务API,请查看此页面

注意:通常不建议使用+。目前的正确版本为6.5.87。更多信息请参见官方博客文章(点击)


不久之前,Medium.com上发布了一篇名为“[DEX] Sky's the limit? No, 65K methods is”的文章(绝对值得一读),介绍了一个可以通过shell脚本来剥离 Google Play服务的方法,你可以在这里(google-play-services-strip-script)找到脚本。
虽然这是一种选择,但也有一个gradle任务可以替您完成此操作:

def toCamelCase(String string) {
    String result = ""
    string.findAll("[^\\W]+") { String word ->
        result += word.capitalize()
    }
    return result
}

afterEvaluate { project ->
    Configuration runtimeConfiguration = project.configurations.getByName('compile')
    ResolutionResult resolution = runtimeConfiguration.incoming.resolutionResult
// Forces resolve of configuration
    ModuleVersionIdentifier module = resolution.getAllComponents().find { it.moduleVersion.name.equals("play-services") }.moduleVersion

    String prepareTaskName = "prepare${toCamelCase("${module.group} ${module.name} ${module.version}")}Library"
    File playServiceRootFolder = project.tasks.find { it.name.equals(prepareTaskName) }.explodedDir

    Task stripPlayServices = project.tasks.create(name: 'stripPlayServices', group: "Strip") {
        inputs.files new File(playServiceRootFolder, "classes.jar")
        outputs.dir playServiceRootFolder
        description 'Strip useless packages from Google Play Services library to avoid reaching dex limit'

        doLast {
            copy {
                from(file(new File(playServiceRootFolder, "classes.jar")))
                into(file(playServiceRootFolder))
                rename { fileName ->
                    fileName = "classes_orig.jar"
                }
            }
            tasks.create(name: "stripPlayServices" + module.version, type: Jar) {
                destinationDir = playServiceRootFolder
                archiveName = "classes.jar"
                from(zipTree(new File(playServiceRootFolder, "classes_orig.jar"))) {
  ----->                // Specify what should be removed
                }
            }.execute()
            delete {
                delete (file(new File(playServiceRootFolder, "classes_orig.jar")))
            }
        }
    }

    project.tasks.findAll { it.name.startsWith('prepare') && it.name.endsWith('Dependencies') }.each { Task task ->
        task.dependsOn stripPlayServices
    }
}

注: 这是从GitHubGist上的Gradle任务中获取的,该任务用于删除Google Play服务库中未使用的软件包。

对你来说相关的部分在箭头所指示的task.create(...)中。在那里,您需要指定应该删除哪些部分。因此,在您的情况下,请在其中编写以下内容:

exclude "com/google/ads/**"
exclude "com/google/android/gms/analytics/**"
exclude "com/google/android/gms/games/**"
exclude "com/google/android/gms/panorama/**"
exclude "com/google/android/gms/plus/**"
exclude "com/google/android/gms/drive/**"
exclude "com/google/android/gms/ads/**"
exclude "com/google/android/gms/wallet/**"
exclude "com/google/android/gms/wearable/**"

这将删除除Maps和GCM部分以外的所有内容。

注意:要使用它,只需将gradle任务的内容复制到您的应用程序模块的build.gradle文件的底部。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接