Pengembangan iteratif yang cepat untuk Android
Halaman ini menjelaskan cara bazel mobile-install membuat pengembangan iteratif untuk Android menjadi jauh lebih cepat. Halaman ini menjelaskan manfaat pendekatan ini dibandingkan dengan tantangan metode penginstalan aplikasi tradisional.
Ringkasan
Untuk menginstal perubahan kecil pada aplikasi Android dengan sangat cepat, lakukan hal berikut:
- Temukan aturan
android_binaryaplikasi yang ingin Anda instal. - Nonaktifkan Proguard dengan menghapus atribut
proguard_specs. - Tetapkan atribut
multidexkenative. - Tetapkan atribut
dex_shardske10. - Hubungkan perangkat Anda yang menjalankan ART (bukan Dalvik) melalui USB dan aktifkan proses debug USB di perangkat tersebut.
- Jalankan
bazel mobile-install :your_target. Startup aplikasi akan sedikit lebih lambat dari biasanya. - Edit kode atau resource Android.
- Jalankan
bazel mobile-install --incremental :your_target. - Anda tidak perlu menunggu lama.
Beberapa opsi command line untuk Bazel yang mungkin berguna:
--adbmemberi tahu Bazel biner adb mana yang akan digunakan--adb_argdapat digunakan untuk menambahkan argumen tambahan ke command lineadb. Salah satu aplikasi yang berguna adalah memilih perangkat yang ingin Anda instal jika Anda memiliki beberapa perangkat yang terhubung ke workstation Anda:bazel mobile-install --adb_arg=-s --adb_arg=<SERIAL> :your_target--start_appotomatis memulai aplikasi
Jika ragu, lihat contohnya atau hubungi kami.
Pengantar
Salah satu atribut terpenting dari toolchain developer adalah kecepatan: ada perbedaan besar antara mengubah kode dan melihatnya berjalan dalam satu detik dengan harus menunggu beberapa menit, terkadang berjam-jam, sebelum Anda mendapatkan masukan tentang apakah perubahan Anda melakukan apa yang Anda harapkan.
Sayangnya, toolchain Android tradisional untuk membuat .apk memerlukan banyak langkah monolitik dan berurutan, dan semuanya harus dilakukan untuk membuat aplikasi Android. Di Google, menunggu lima menit untuk membuat perubahan satu baris tidak jarang terjadi pada project yang lebih besar seperti Google Maps.
bazel mobile-install membuat pengembangan iteratif untuk Android jauh lebih cepat dengan menggunakan kombinasi pemangkasan perubahan, sharding tugas, dan manipulasi cerdas internal Android, tanpa mengubah kode aplikasi Anda.
Masalah dengan penginstalan aplikasi tradisional
Membuat aplikasi Android memiliki beberapa masalah, termasuk:
Dexing. Secara default, "dx" dipanggil tepat satu kali dalam build dan tidak tahu cara menggunakan kembali tugas dari build sebelumnya: dx mengulang setiap metode, meskipun hanya satu metode yang diubah.
Mengupload data ke perangkat. adb tidak menggunakan bandwidth penuh koneksi USB 2.0, dan aplikasi yang lebih besar dapat memerlukan banyak waktu untuk diupload. Seluruh aplikasi diupload, meskipun hanya bagian kecil yang diubah, misalnya, resource atau satu metode, sehingga hal ini dapat menjadi hambatan utama.
Kompilasi ke kode native. Android L memperkenalkan ART, runtime Android baru, yang mengompilasi aplikasi ahead-of-time, bukan mengompilasinya just-in-time seperti Dalvik. Hal ini membuat aplikasi jauh lebih cepat dengan biaya waktu penginstalan yang lebih lama. Ini adalah kompromi yang baik bagi pengguna karena mereka biasanya menginstal aplikasi satu kali dan menggunakannya berkali-kali, tetapi menghasilkan pengembangan yang lebih lambat saat aplikasi diinstal berkali-kali dan setiap versi dijalankan paling banyak beberapa kali.
Pendekatan bazel mobile-install
bazel mobile-install membuat peningkatan berikut:
Dexing dengan sharding. Setelah membuat kode Java aplikasi, Bazel membagi file class menjadi bagian-bagian berukuran kira-kira sama dan memanggil
dxsecara terpisah.dxtidak dipanggil pada shard yang tidak berubah sejak build terakhir.Transfer file inkremental. Resource Android, file .dex, dan library native dihapus dari .apk utama dan disimpan di bawah direktori mobile-install terpisah. Hal ini memungkinkan Anda mengupdate kode dan resource Android secara independen tanpa menginstal ulang seluruh aplikasi. Dengan demikian, transfer file memerlukan waktu lebih sedikit dan hanya file .dex yang diubah yang dikompilasi ulang di perangkat.
Memuat bagian aplikasi dari luar .apk. Aplikasi stub kecil dimasukkan ke dalam .apk yang memuat resource Android, kode Java, dan kode native dari direktori mobile-install di perangkat, lalu mentransfer kontrol ke aplikasi sebenarnya. Semua ini transparan bagi aplikasi, kecuali dalam beberapa kasus ekstrem yang dijelaskan di bawah.
Dexing dengan Sharding
Dexing dengan sharding cukup mudah: setelah file .jar dibuat, a
alat
akan membaginya menjadi file .jar terpisah dengan ukuran yang kira-kira sama, lalu memanggil
dx pada file yang diubah sejak build sebelumnya. Logika yang menentukan shard mana yang akan di-dex tidak khusus untuk Android: logika tersebut hanya menggunakan algoritma pemangkasan perubahan umum Bazel.
Versi pertama algoritma sharding hanya mengurutkan file .class menurut abjad, lalu membagi daftar menjadi bagian-bagian berukuran sama, tetapi hal ini terbukti tidak optimal: jika class ditambahkan atau dihapus (bahkan class bertingkat atau anonim), semua class setelahnya menurut abjad akan bergeser satu, sehingga shard tersebut akan di-dex lagi. Oleh karena itu, diputuskan untuk membagi paket Java, bukan class individual. Tentu saja, hal ini masih menghasilkan banyak shard jika paket baru ditambahkan atau dihapus, tetapi hal itu jauh lebih jarang terjadi daripada menambahkan atau menghapus satu class.
Jumlah shard dikontrol oleh file BUILD (menggunakan atribut android_binary.dex_shards). Dalam kondisi ideal, Bazel akan otomatis menentukan jumlah shard yang terbaik, tetapi Bazel saat ini harus mengetahui kumpulan tindakan (misalnya, perintah yang akan dieksekusi selama build) sebelum mengeksekusi salah satunya, sehingga Bazel tidak dapat menentukan jumlah shard yang optimal karena tidak mengetahui jumlah class Java yang akan ada di aplikasi. Secara umum, semakin banyak shard, semakin cepat build dan penginstalan, tetapi startup aplikasi menjadi lebih lambat, karena dynamic linker harus melakukan lebih banyak pekerjaan. Jumlah shard yang ideal biasanya antara 10 dan 50.
Transfer file inkremental
Setelah membuat aplikasi, langkah berikutnya adalah menginstalnya, sebaiknya dengan upaya sesedikit mungkin. Penginstalan terdiri dari langkah-langkah berikut:
- Menginstal .apk (biasanya menggunakan
adb install) - Mengupload file .dex, resource Android, dan library native ke direktori mobile-install
Tidak banyak inkrementalitas pada langkah pertama: aplikasi diinstal atau tidak. Bazel saat ini mengandalkan pengguna untuk menunjukkan apakah langkah ini harus dilakukan melalui opsi command line --incremental karena Bazel tidak dapat menentukan dalam semua kasus apakah langkah ini diperlukan.
Pada langkah kedua, file aplikasi dari build dibandingkan dengan file manifes di perangkat yang mencantumkan file aplikasi yang ada di perangkat dan checksum-nya. File baru diupload ke perangkat, file yang diubah diupdate, dan file yang dihapus akan dihapus dari perangkat. Jika manifes tidak ada, diasumsikan bahwa setiap file perlu diupload.
Perhatikan bahwa algoritma penginstalan inkremental dapat ditipu dengan mengubah file di perangkat, tetapi tidak mengubah checksum-nya dalam manifes. Hal ini dapat dicegah dengan menghitung checksum file di perangkat, tetapi hal ini dianggap tidak sebanding dengan peningkatan waktu penginstalan.
Aplikasi Stub
Aplikasi stub adalah tempat terjadinya proses pemuatan dex, kode native, dan resource Android dari direktori mobile-install di perangkat.
Pemuatan sebenarnya diimplementasikan dengan membuat subclass BaseDexClassLoader dan merupakan teknik yang cukup terdokumentasi dengan baik. Hal ini terjadi sebelum class aplikasi dimuat, sehingga class aplikasi yang ada di apk dapat ditempatkan di direktori mobile-install di perangkat sehingga dapat diupdate tanpa adb install.
Hal ini harus terjadi sebelum class aplikasi dimuat, sehingga tidak ada class aplikasi yang perlu berada di .apk, yang berarti perubahan pada class tersebut akan memerlukan penginstalan ulang penuh.
Hal ini dicapai dengan mengganti class Application yang ditentukan di
AndroidManifest.xml dengan aplikasi
stub. Hal ini mengambil kontrol saat aplikasi dimulai, dan menyesuaikan pemuat class dan pengelola resource dengan tepat pada saat paling awal (konstruktornya) menggunakan refleksi Java pada internal framework Android.
Hal lain yang dilakukan aplikasi stub adalah menyalin library native yang diinstal oleh mobile-install ke lokasi lain. Hal ini diperlukan karena dynamic linker memerlukan bit X untuk ditetapkan pada file, yang tidak dapat dilakukan untuk lokasi mana pun yang dapat diakses oleh adb non-root.
Setelah semua hal ini selesai, aplikasi stub kemudian membuat instance class Application yang sebenarnya, mengubah semua referensi ke dirinya sendiri ke aplikasi sebenarnya dalam framework Android.
Hasil
Performa
Secara umum, bazel mobile-install menghasilkan peningkatan kecepatan 4x hingga 10x dalam membangun dan menginstal aplikasi besar setelah perubahan kecil.
Angka berikut dihitung untuk beberapa produk Google:
Hal ini tentu saja bergantung pada sifat perubahan: kompilasi ulang setelah mengubah library dasar memerlukan lebih banyak waktu.
Batasan
Trik yang dimainkan aplikasi stub tidak berfungsi dalam setiap kasus. Kasus berikut menunjukkan tempat aplikasi stub tidak berfungsi seperti yang diharapkan:
Saat
Contextditransmisikan ke classApplicationdiContentProvider#onCreate(). Metode ini dipanggil selama startup aplikasi sebelum kita memiliki kesempatan untuk mengganti instance classApplication, sehinggaContentProviderakan tetap mereferensikan aplikasi stub, bukan aplikasi yang sebenarnya. Mungkin, ini bukan bug karena Anda tidak seharusnya melakukan downcastContextseperti ini, tetapi hal ini tampaknya terjadi di beberapa aplikasi di Google.Resource yang diinstal oleh
bazel mobile-installhanya tersedia dari dalam aplikasi. Jika resource diakses oleh aplikasi lain melaluiPackageManager#getApplicationResources(), resource ini akan berasal dari penginstalan non-inkremental terakhir.Perangkat yang tidak menjalankan ART. Meskipun aplikasi stub berfungsi dengan baik di Froyo dan yang lebih baru, Dalvik memiliki bug yang membuatnya mengira bahwa aplikasi salah jika kodenya didistribusikan ke beberapa file .dex dalam kasus tertentu, misalnya, saat anotasi Java digunakan dengan cara tertentu. Selama aplikasi Anda tidak memicu bug ini, aplikasi tersebut juga akan berfungsi dengan Dalvik (namun, perhatikan bahwa dukungan untuk versi Android lama bukanlah fokus kami)