Pengembangan iteratif yang cepat untuk Android
Halaman ini menjelaskan cara bazel mobile-install
membuat pengembangan berulang
untuk Android dengan jauh lebih cepat. Menjelaskan manfaat pendekatan ini dibandingkan
tantangan metode pemasangan aplikasi tradisional.
Ringkasan
Untuk menginstal perubahan kecil ke aplikasi Android dengan sangat cepat, lakukan hal berikut:
- Temukan aturan
android_binary
aplikasi yang ingin Anda instal. - Nonaktifkan Proguard dengan menghapus atribut
proguard_specs
. - Tetapkan atribut
multidex
kenative
. - Tetapkan atribut
dex_shards
ke10
. - Sambungkan perangkat Anda yang menjalankan ART (bukan Dalvik) melalui USB dan aktifkan USB melakukan proses debug padanya.
- Jalankan
bazel mobile-install :your_target
. {i>App startup<i} akan sedikit lebih lambat dari biasanya. - Edit kode atau resource Android.
- Jalankan
bazel mobile-install --incremental :your_target
. - Nikmati tanpa perlu menunggu banyak.
Beberapa opsi command line ke Bazel yang mungkin berguna:
--adb
memberi tahu Bazel biner adb mana yang akan digunakan--adb_arg
dapat digunakan untuk menambahkan argumen tambahan ke command lineadb
. Satu aplikasi yang berguna dari hal ini adalah memilih perangkat mana yang ingin Anda instal jika Anda memiliki beberapa perangkat yang terhubung ke workstation:bazel mobile-install --adb_arg=-s --adb_arg=<SERIAL> :your_target
--start_app
otomatis memulai aplikasi
Jika ragu, lihat contoh atau hubungi kami.
Pengantar
Salah satu atribut terpenting dari toolchain developer adalah kecepatan: ada adalah perbedaan antara mengubah kode dan melihatnya berjalan dalam beberapa menit dan terkadang berjam-jam, sebelum Anda mendapatkan umpan balik pada apakah perubahan yang Anda lakukan sesuai dengan yang Anda harapkan.
Sayangnya, toolchain Android tradisional untuk mem-build .apk memerlukan banyak langkah monolitik dan berurutan, dan semua ini harus dilakukan untuk mem-build aplikasi Android. Di Google, menunggu lima menit untuk mem-build perubahan satu baris bukanlah hal yang aneh 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 pekerjaan, dan manipulasi cerdas
Internal Android, semuanya tanpa mengubah kode aplikasi Anda.
Masalah terkait penginstalan aplikasi tradisional
Membangun aplikasi Android memiliki beberapa masalah, termasuk:
Dexing. Secara default, "dx" dipanggil sekali saja dalam build dan mengetahui cara menggunakan kembali pekerjaan dari build sebelumnya: ia melakukan dexes setiap metode lagi, bahkan meskipun hanya satu metode yang diubah.
Mengupload data ke perangkat. adb tidak menggunakan bandwidth penuh USB 2.0 koneksi internet, dan aplikasi yang lebih besar dapat memakan banyak waktu. Seluruh aplikasi diupload, meskipun hanya sebagian kecil yang berubah, misalnya, aset atau metode tunggal, sehingga ini bisa menjadi bottleneck utama.
Kompilasi ke kode native. Android L memperkenalkan ART, runtime Android baru, yang mengompilasi aplikasi terlebih dahulu, bukan mengompilasi aplikasi tepat waktu seperti Dalvik. Hal ini membuat aplikasi jauh lebih cepat dengan biaya penginstalan yang lebih lama baik. Ini adalah kompromi yang baik bagi pengguna karena mereka biasanya menginstal aplikasi sekali 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
melakukan peningkatan berikut:
Dexing dengan sharding. Setelah mem-build kode Java aplikasi, Bazel akan mengelompokkan file class menjadi bagian yang berukuran kira-kira sama dan memanggil
dx
secara terpisah di atasnya.dx
tidak 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 dalam direktori penginstalan seluler terpisah. Hal ini memungkinkan update kode dan Android sumber daya secara independen tanpa menginstal ulang seluruh aplikasi. Dengan demikian, mentransfer file membutuhkan lebih sedikit waktu dan hanya file .dex yang memiliki diubah dikompilasi ulang di perangkat.
Memuat bagian aplikasi dari luar .apk. Sebuah aplikasi stub kecil dimasukkan ke dalam .apk yang memuat resource Android, kode Java, dan kode native dari direktori penginstalan seluler di perangkat, lalu mentransfer kontrol ke aplikasi yang sebenarnya. Semua ini transparan untuk aplikasi, kecuali dalam beberapa kasus sudut yang dijelaskan di bawah.
Dexing dengan Shard
Dexing dengan shard cukup mudah: setelah file .jar di-build,
alat
akan me-shard-nya 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 ini hanya menggunakan
algoritma pemangkasan perubahan umum Bazel.
Versi pertama algoritma sharding hanya mengurutkan file .class menurut abjad, lalu membagi daftar itu menjadi bagian-bagian yang berukuran sama, tetapi ini terbukti kurang optimal: jika class ditambahkan atau dihapus (bahkan class bertingkat atau anonim satu), hal itu akan menyebabkan semua kelas berdasarkan abjad setelahnya bergeser satu, yang mengakibatkan hasil dexing pada shard tersebut lagi. Oleh karena itu, diputuskan untuk melakukan sharding Java paket, bukan class individual. Tentu saja, hal ini masih menghasilkan de-ekstensi banyak shard jika paket baru ditambahkan atau dihapus, tetapi hal ini jauh lebih jarang dibandingkan dengan menambahkan atau menghapus satu class.
Jumlah shard dikontrol oleh file BUILD (menggunakan
android_binary.dex_shards
). Dalam kondisi ideal, Bazel akan
otomatis menentukan jumlah shard yang terbaik, tetapi saat ini Bazel harus mengetahui
kumpulan tindakan (misalnya, perintah yang akan dieksekusi selama build) sebelum
mengeksekusi salah satunya, sehingga tidak dapat menentukan jumlah shard yang optimal
karena tidak mengetahui jumlah class Java yang pada akhirnya akan ada di
aplikasi. Secara umum, semakin banyak shard, semakin cepat build dan
penginstalan, tetapi semakin lambat startup aplikasi, karena penaut
dinamis harus melakukan lebih banyak pekerjaan. Titik optimal biasanya antara 10 dan 50 shard.
Transfer file inkremental
Setelah membuat aplikasi, langkah berikutnya adalah menginstalnya, sebaiknya dengan upaya yang paling sedikit. Penginstalan terdiri dari langkah-langkah berikut:
- Menginstal .apk (biasanya menggunakan
adb install
) - Mengupload file .dex, resource Android, dan library native ke direktori instal seluler
Tidak banyak inkrementalitas di langkah pertama: aplikasi sudah diinstal
atau tidak. Bazel saat ini mengandalkan pengguna untuk menunjukkan apakah harus melakukan langkah ini
melalui opsi command line --incremental
karena tidak dapat menentukan dalam
semua kasus apakah diperlukan atau tidak.
Pada langkah kedua, file aplikasi dari build dibandingkan dengan file manifes di perangkat yang mencantumkan file aplikasi mana yang ada di perangkat dan checksum-nya. Setiap file baru akan diupload ke perangkat, setiap file yang telah berubah akan diperbarui, dan setiap file yang telah dihapus akan dihapus dari perangkat. Jika manifes tidak ada, setiap file diasumsikan harus diupload.
Perhatikan bahwa Anda dapat mengelabui algoritma penginstalan inkremental dengan mengubah file di perangkat, tetapi tidak checksum-nya dalam manifes. Hal ini dapat telah diamanatkan dengan menghitung {i>checksum<i} file pada tapi ini dianggap tidak sepadan dengan peningkatan waktu instalasinya.
Aplikasi Stub
Aplikasi stub adalah tempat terjadinya keajaiban untuk memuat dex, kode native, dan
resource Android dari direktori mobile-install
di perangkat.
Pemuatan yang sebenarnya diimplementasikan dengan membuat subclass BaseDexClassLoader
dan merupakan
teknik yang cukup terdokumentasi dengan baik. Hal ini terjadi sebelum class
aplikasi dimuat, sehingga class aplikasi apa pun 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 bahwa perubahan pada class tersebut akan memerlukan penginstalan ulang secara penuh.
Hal ini dilakukan dengan mengganti class Application
yang ditentukan di
AndroidManifest.xml
dengan
aplikasi stub. Ini
mengontrol kapan aplikasi dimulai, dan menyesuaikan loader class dan
pengelola sumber daya 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 penginstalan seluler ke lokasi lain. Hal ini diperlukan karena
penaut dinamis memerlukan bit X
untuk ditetapkan pada file, yang tidak dapat
dilakukan untuk lokasi apa pun yang dapat diakses oleh adb
non-root.
Setelah semua ini selesai, aplikasi stub kemudian membuat instance
Application
yang sebenarnya, mengubah semua referensi ke dirinya sendiri menjadi
aplikasi dalam framework Android.
Hasil
Performa
Secara umum, bazel mobile-install
menghasilkan peningkatan kecepatan build
dan penginstalan aplikasi besar sebesar 4x hingga 10x setelah perubahan kecil.
Angka berikut telah dihitung untuk beberapa produk Google:
Hal ini, tentu saja, tergantung pada sifat perubahan: kompilasi ulang setelah mengubah library dasar akan membutuhkan waktu lebih lama.
Batasan
Trik yang dimainkan aplikasi stub tidak berfungsi dalam setiap kasus. Kasus berikut akan menunjukkan area yang tidak berfungsi seperti yang diharapkan:
Saat
Context
ditransmisikan ke classApplication
diContentProvider#onCreate()
. Metode ini dipanggil selama aplikasi startup sebelum kita sempat mengganti instanceApplication
sehinggaContentProvider
akan tetap merujuk ke aplikasi stub bukannya yang asli. Bisa dibilang, ini bukan bug karena Anda tidak harus mendowncastContext
seperti ini, tetapi tampaknya hal ini terjadi di beberapa aplikasi di Google.Resource yang diinstal oleh
bazel mobile-install
hanya 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. Sementara aplikasi stub bekerja dengan baik pada Froyo dan setelahnya, Dalvik memiliki {i>bug<i} yang membuatnya berpikir bahwa aplikasi itu salah jika kodenya didistribusikan ke beberapa file .dex dalam contohnya, ketika anotasi Java digunakan dalam spesifik sebelumnya. Selama aplikasi Anda tidak menggelitik bug, aplikasi seharusnya berfungsi dengan Dalvik, juga (perhatikan bahwa dukungan untuk versi Android lama bukanlah fokus)