【问题标题】:Limit of methods 64K per a dex file in AndroidAndroid 中每个 dex 文件的方法限制为 64K
【发布时间】:2014-12-10 02:26:57
【问题描述】:

我遇到了这个问题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"
    }

我认为这个sn-p的代码是错误的,因为有错误method ID not in [0, 0xffff]...。如何排除 Google Play 服务的不必要部分?我只使用地图和 GCM。

已更新。

感谢反向。这是非常有用的代码。有一个用于获取方法计数的脚本(也可以查看现有包的名称)https://gist.github.com/JakeWharton/6002797 (source ./dex.sh; dex-method-count-by-package test.apk)

在使用 reVerse 答案中的代码的 sn-p 之前

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答案中的sn-p代码后,包:广告,游戏等被删除。

【问题讨论】:

    标签: android android-gradle-plugin


    【解决方案1】:

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

    在 6.5 版中,Google 终于解绑了 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,请检查 this page on d.android.com

    注意:通常不鼓励使用+。截至目前,当前正确的版本是6.5.87。如需更多信息,请参阅official Blog-Post (click)


    前段时间,Medium.com 上有一篇名为 "[DEX] Sky’s the limit? No, 65K methods is" 的文章(绝对值得一读),其中描述了一种剥离 Google 的方法使用 shell 脚本播放服务,您可以找到 here (google-play-services-strip-script)
    虽然这是一个选项,但还有一个 gradle task 可以为您执行此操作:

    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
        }
    }
    

    注意:这取自Gradle task to strip unused packages on Google Play Services library @GitHubGist

    与您相关的部分是箭头在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-Part 之外的所有内容。

    注意:要使用它,只需将 gradle-task 的内容复制到您的应用模块的build.gradle 文件的底部即可。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-22
      • 2014-12-09
      • 1970-01-01
      • 2017-07-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多