91

多くのライブラリ プロジェクトに依存するかなり大きな Android アプリがあります。Android コンパイラには .dex ファイルごとに 65536 メソッドという制限があり、私はその数を超えています。

メソッドの制限に達したときに選択できる (少なくとも私が知っている) 基本的に 2 つのパスがあります。

1) コードを縮小する

2) 複数の dex ファイルを作成する (このブログ投稿を参照)

私は両方を調査し、メソッド数が非常に多くなる原因を突き止めようとしました。Google Drive API は、12,000 を超える Guava 依存関係で最大のチャンクを占めます。Drive API v2 の合計ライブラリ数が 23,000 を超えました!

私の質問だと思いますが、私は何をすべきだと思いますか? アプリの機能として Google ドライブの統合を削除する必要がありますか? API を縮小する方法はありますか (はい、proguard を使用しています)。複数の dex ルートを使用する必要がありますか (これは、特にサード パーティの API を扱う場合、かなり面倒に見えます)。

4

12 に答える 12

53

そのために multidex サポート ライブラリを使用できます。multidex を有効にするに

1)依存関係に含める:

dependencies {
  ...
  compile 'com.android.support:multidex:1.0.0'
}

2)アプリで有効にします。

defaultConfig {
    ...
    minSdkVersion 14
    targetSdkVersion 21
    ....
    multiDexEnabled true
}

3)アプリのアプリケーションクラスがある場合は、次のようにattachBaseContextメソッドをオーバーライドします。

package ....;
...
import android.support.multidex.MultiDex;

public class MyApplication extends Application {
  ....
   @Override
   protected void attachBaseContext(Context context) {
    super.attachBaseContext(context);
    MultiDex.install(this);
   }
}

4)アプリケーションのアプリケーションクラスがない場合は、android.support.multidex.MultiDexApplicationをアプリケーションとしてマニフェスト ファイルに登録します。このような:

<application
    ...
    android:name="android.support.multidex.MultiDexApplication">
    ...
</application>

そしてそれはうまくいくはずです!

于 2015-04-12T18:02:06.437 に答える
9

6.5 より前のバージョンの Google Play サービスでは、API のパッケージ全体をアプリにコンパイルする必要がありました。場合によっては、これを行うと、アプリ内のメソッド (フレームワーク API、ライブラリ メソッド、および独自のコードを含む) の数を 65,536 の制限未満に抑えることがより困難になります。

バージョン 6.5 以降では、代わりに Google Play サービス API を選択してアプリにコンパイルできます。たとえば、Google Fit API と Android Wear API のみを含めるには、build.gradle ファイルの次の行を置き換えます。

compile 'com.google.android.gms:play-services:6.5.87'

これらの行で:

compile 'com.google.android.gms:play-services-fitness:6.5.87'
compile 'com.google.android.gms:play-services-wearable:6.5.87'

詳細については、ここをクリックしてください。

于 2015-02-27T08:31:38.770 に答える
7

使用されていないメソッドは最終ビルドに含まれないため、proguard を使用して apk を軽量化します。グアバでプロガードを使用するには、プロガード構成ファイルで次のことを再確認してください(これが既にある場合は申し訳ありませんが、執筆時点では知られていませんでした):

# Guava exclusions (http://code.google.com/p/guava-libraries/wiki/UsingProGuardWithGuava)
-dontwarn sun.misc.Unsafe
-dontwarn com.google.common.collect.MinMaxPriorityQueue
-keepclasseswithmembers public class * {
    public static void main(java.lang.String[]);
} 

# Guava depends on the annotation and inject packages for its annotations, keep them both
-keep public class javax.annotation.**
-keep public class javax.inject.**

さらに、ActionbarSherlock を使用している場合、v7 appcompat サポート ライブラリに切り替えると、メソッド数も大幅に削減されます (個人的な経験に基づく)。手順は次の場所にあります。

于 2013-12-20T17:21:27.793 に答える
7

Jar Jar Linksを使用して、Google Play Services (16K メソッド!) のような巨大な外部ライブラリを縮小できます。

あなたの場合common internaldriveサブパッケージを除いてGoogle Play Services jarからすべてをリッピングします。

于 2014-06-15T13:58:24.217 に答える
4

Gradle を使用していない Eclipse ユーザーのために、Google Play Services jar を分解し、必要な部分だけで再構築するツールがあります。

私はdextorer で strip_play_services.shを使用しています。

いくつかの内部依存関係があるため、どのサービスを含めるかを正確に知るのは難しい場合がありますが、必要なものが不足していることが判明した場合は、小さく始めて構成に追加できます。

于 2015-04-24T13:20:14.353 に答える
3

長期的には、アプリを複数の dex に分割することが最善の方法だと思います。

于 2013-12-09T22:06:21.823 に答える
2

Multi-dex のサポートは、この問題に対する公式の解決策になる予定です。詳細については、こちらの回答を参照してください。

于 2014-10-04T19:39:13.630 に答える
2

ビルドプロセスを非常に遅くするmultidexを使用しない場合。次のことができます。yahska述べたように、特定の Google Play サービス ライブラリを使用します。ほとんどの場合、これだけが必要です。

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

ここに利用可能なすべてのパッケージがありますAPI を実行可能ファイルに選択的にコンパイルする

これで十分でない場合は、gradle スクリプトを使用できます。このコードをファイル「strip_play_services.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')
println runtimeConfiguration
ResolutionResult resolution = runtimeConfiguration.incoming.resolutionResult
// Forces resolve of configuration
ModuleVersionIdentifier module = resolution.getAllComponents().find {
    it.moduleVersion.name.equals("play-services")
}.moduleVersion


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


def tmpDir = new File(project.buildDir, 'intermediates/tmp')
tmpDir.mkdirs()
def libFile = new File(tmpDir, "${playServicesLibName}.marker")

def strippedClassFileName = "${playServicesLibName}.jar"
def classesStrippedJar = new File(tmpDir, strippedClassFileName)

def packageToExclude = ["com/google/ads/**",
                        "com/google/android/gms/actions/**",
                        "com/google/android/gms/ads/**",
                        // "com/google/android/gms/analytics/**",
                        "com/google/android/gms/appindexing/**",
                        "com/google/android/gms/appstate/**",
                        "com/google/android/gms/auth/**",
                        "com/google/android/gms/cast/**",
                        "com/google/android/gms/drive/**",
                        "com/google/android/gms/fitness/**",
                        "com/google/android/gms/games/**",
                        "com/google/android/gms/gcm/**",
                        "com/google/android/gms/identity/**",
                        "com/google/android/gms/location/**",
                        "com/google/android/gms/maps/**",
                        "com/google/android/gms/panorama/**",
                        "com/google/android/gms/plus/**",
                        "com/google/android/gms/security/**",
                        "com/google/android/gms/tagmanager/**",
                        "com/google/android/gms/wallet/**",
                        "com/google/android/gms/wearable/**"]

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 {
        def packageExcludesAsString = packageToExclude.join(",")
        if (libFile.exists()
                && libFile.text == packageExcludesAsString
                && classesStrippedJar.exists()) {
            println "Play services already stripped"
            copy {
                from(file(classesStrippedJar))
                into(file(playServiceRootFolder))
                rename { fileName ->
                    fileName = "classes.jar"
                }
            }
        } else {
            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"))) {
                    exclude packageToExclude
                }
            }.execute()
            delete file(new File(playServiceRootFolder, "classes_orig.jar"))
            copy {
                from(file(new File(playServiceRootFolder, "classes.jar")))
                into(file(tmpDir))
                rename { fileName ->
                    fileName = strippedClassFileName
                }
            }
            libFile.text = packageExcludesAsString
        }
    }
}

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

}

次に、このスクリプトを build.gradle に適用します。

apply plugin: 'com.android.application'
apply from: 'strip_play_services.gradle'
于 2015-04-23T07:42:10.767 に答える
1

Google Play Services を使用している場合は、20,000 以上のメソッドが追加されていることをご存知かもしれません。すでに述べたように、Android Studio には特定のサービスをモジュールに含めるオプションがありますが、Eclipse にこだわるユーザーはモジュール化を自分の手に委ねる必要があります :(

幸いなことに、この作業をかなり簡単にするシェル スクリプトがあります。Google Play サービスの jar ディレクトリに展開し、提供された .conf ファイルを必要に応じて編集して、シェル スクリプトを実行するだけです。

その使用例はこちらです。

compile 'com.google.android.gms:play-services:9.0.0'彼が言ったように、私は必要なライブラリだけを置き換え、それはうまくいきました。

于 2016-05-22T13:52:39.220 に答える
1

Google Play Services を使用している場合は、20,000 以上のメソッドが追加されていることをご存知かもしれません。すでに述べたように、Android Studio には特定のサービスをモジュール式に含めるオプションがありますが、Eclipse にこだわるユーザーはモジュール化を自分の手に委ねる必要があります :(

幸いなことに、この作業をかなり簡単にするシェル スクリプトがあります。Google Play サービスの jar ディレクトリに展開し、提供された .conf ファイルを必要に応じて編集して、シェル スクリプトを実行するだけです。

その使用例はこちらです。

于 2015-05-28T03:19:44.543 に答える