Saya memiliki aplikasi Android yang agak besar yang bergantung pada banyak proyek perpustakaan. Kompiler Android memiliki batasan 65536 metode per file .dex dan saya melampaui angka itu.
Pada dasarnya ada dua jalur yang dapat Anda pilih (setidaknya yang saya tahu) ketika Anda mencapai batas metode.
1) Kecilkan kode Anda
2) Bangun beberapa file dex ( lihat posting blog ini )
Saya melihat keduanya dan mencoba mencari tahu apa yang menyebabkan jumlah metode saya menjadi begitu tinggi. Google Drive API mengambil bagian terbesar dengan ketergantungan Guava di lebih dari 12.000. Total libs untuk Drive API v2 mencapai lebih dari 23.000!
Pertanyaan saya, menurut Anda, apa yang harus saya lakukan? Haruskah saya menghapus integrasi Google Drive sebagai fitur aplikasi saya? Apakah ada cara untuk mengecilkan API (ya, saya menggunakan proguard)? Haruskah saya menggunakan beberapa rute dex (yang terlihat agak menyakitkan, terutama berurusan dengan API pihak ketiga)?
sumber
apk
bentuk semu ? Saya pribadi ingin melihat integrasi DriveJawaban:
Sepertinya Google akhirnya menerapkan solusi / perbaikan untuk melampaui batas metode 65K file dex.
Lihat: Membangun Aplikasi dengan Lebih dari 65K Metode
Anda tetap harus menghindari mencapai batas metode 65K dengan secara aktif menggunakan proguard dan meninjau dependensi Anda.
sumber
Anda dapat menggunakan pustaka dukungan multidex untuk itu, untuk mengaktifkan multidex
1) sertakan dalam dependensi:
dependencies { ... compile 'com.android.support:multidex:1.0.0' }
2) Aktifkan di aplikasi Anda:
defaultConfig { ... minSdkVersion 14 targetSdkVersion 21 .... multiDexEnabled true }
3) jika Anda memiliki kelas aplikasi untuk aplikasi Anda, maka Ganti metode attachBaseContext seperti ini:
package ....; ... import android.support.multidex.MultiDex; public class MyApplication extends Application { .... @Override protected void attachBaseContext(Context context) { super.attachBaseContext(context); MultiDex.install(this); } }
4) jika Anda tidak memiliki kelas aplikasi untuk aplikasi Anda, maka daftarkan android.support.multidex.MultiDexApplication sebagai aplikasi Anda dalam file manifes. seperti ini:
<application ... android:name="android.support.multidex.MultiDexApplication"> ... </application>
dan itu akan bekerja dengan baik!
sumber
Play Services
6.5+ membantu: http://android-developers.blogspot.com/2014/12/google-play-services-and-dex-method.html...
Ini adalah kabar baik, untuk game sederhana misalnya Anda mungkin hanya membutuhkan
base
,games
dan mungkindrive
.sumber
Dalam versi layanan Google Play sebelum 6.5, Anda harus mengompilasi seluruh paket API ke dalam aplikasi Anda. Dalam beberapa kasus, hal itu membuat lebih sulit untuk mempertahankan jumlah metode dalam aplikasi Anda (termasuk API kerangka kerja, metode pustaka, dan kode Anda sendiri) di bawah batas 65.536.
Dari versi 6.5, sebagai gantinya Anda dapat secara selektif menyusun API layanan Google Play ke dalam aplikasi Anda. Misalnya, untuk hanya menyertakan Google Fit dan Android Wear API, ganti baris berikut dalam file build.gradle Anda:
compile 'com.google.android.gms:play-services:6.5.87'
dengan garis-garis ini:
compile 'com.google.android.gms:play-services-fitness:6.5.87' compile 'com.google.android.gms:play-services-wearable:6.5.87'
untuk referensi lebih lanjut, Anda bisa klik di sini
sumber
Gunakan proguard untuk meringankan apk Anda karena metode yang tidak digunakan tidak akan ada dalam versi final Anda. Periksa kembali apakah Anda telah mengikuti di file konfigurasi proguard Anda untuk menggunakan proguard dengan jambu biji (maaf jika Anda sudah memiliki ini, tidak diketahui pada saat penulisan):
# 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.**
Selain itu, jika Anda menggunakan ActionbarSherlock, beralih ke pustaka dukungan appcompat v7 juga akan banyak mengurangi jumlah metode Anda (berdasarkan pengalaman pribadi). Instruksi berada:
sumber
Warning: butterknife.internal.ButterKnifeProcessor: can't find superclass or interface javax.annotation.processing.AbstractProcessor
saat berlari./gradlew :myapp:proguardDevDebug
Anda dapat menggunakan Jar Jar Links untuk mengecilkan perpustakaan eksternal yang besar seperti Google Play Services (metode 16K!)
Dalam kasus Anda, Anda hanya akan merobek semuanya dari jar Layanan Google Play kecuali
common
internal
dandrive
sub-paket.sumber
Untuk pengguna Eclipse yang tidak menggunakan Gradle, ada alat yang akan memecah tabung Layanan Google Play dan membuatnya kembali hanya dengan bagian yang Anda inginkan.
Saya menggunakan strip_play_services.sh oleh dextorer .
Mungkin sulit untuk mengetahui dengan tepat layanan mana yang akan disertakan karena ada beberapa dependensi internal tetapi Anda dapat memulai dari yang kecil dan menambahkan ke konfigurasi jika ternyata ada hal-hal yang diperlukan hilang.
sumber
Menurut saya, dalam jangka panjang, memecah aplikasi Anda dalam beberapa dex akan menjadi cara terbaik.
sumber
Dukungan multi-dex
akan menjadisolusi resmi untuk masalah ini. Lihat jawaban saya di sini untuk detailnya.sumber
Jika tidak menggunakan multidex yang membuat proses build sangat lambat. Anda dapat melakukan hal berikut. Seperti yang disebutkan yahska gunakan pustaka layanan google play khusus. Untuk kebanyakan kasus, hanya ini yang diperlukan.
compile 'com.google.android.gms:play-services-base:6.5.+'
Ini semua paket yang tersedia Mengompilasi API secara selektif ke dalam file yang dapat dieksekusi
Jika ini tidak cukup, Anda dapat menggunakan skrip gradle. Letakkan kode ini di file '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 }
}
Kemudian terapkan skrip ini di build.gradle Anda, seperti ini
apply plugin: 'com.android.application' apply from: 'strip_play_services.gradle'
sumber
Jika menggunakan Layanan Google Play, Anda mungkin tahu bahwa itu menambahkan metode 20k +. Seperti yang telah disebutkan, Android Studio memiliki opsi untuk penyertaan modular layanan tertentu, tetapi pengguna yang terjebak dengan Eclipse harus mengambil modularisasi ke tangan mereka sendiri :(
Untungnya ada skrip shell yang membuat pekerjaan ini cukup mudah. Cukup ekstrak ke direktori jar layanan google play, edit file .conf yang disediakan sesuai kebutuhan dan jalankan skrip shell.
Contoh penggunaannya ada di sini .
sumber
Seperti yang dia katakan, saya mengganti
compile 'com.google.android.gms:play-services:9.0.0'
hanya dengan perpustakaan yang saya butuhkan dan itu berhasil.sumber