Waktu startup aplikasi

Pengguna mengharapkan aplikasi responsif dan cepat untuk dimuat. Aplikasi dengan waktu mulai lambat tidak akan memenuhi harapan tersebut, dan dapat mengecewakan pengguna. Pengalaman buruk semacam ini dapat menyebabkan pengguna memberikan rating yang buruk terhadap aplikasi Anda di Play Store, atau bahkan berhenti menggunakan aplikasi Anda.

Dokumen ini memberikan informasi yang akan membantu Anda mengoptimalkan waktu peluncuran aplikasi Anda. Diawali dengan menjelaskan internal dari proses peluncuran. Selanjutnya, membahas cara menggambarkan performa startup. Terakhir, menjelaskan beberapa masalah waktu mulai umum, dan memberikan beberapa petunjuk tentang cara mengatasinya.

Memahami konten memulai aplikasi

Peluncuran aplikasi dapat dilakukan dalam salah satu dari tiga kondisi berikut, masing-masing memengaruhi waktu yang diperlukan aplikasi Anda agar terlihat oleh pengguna: start cold, start warm, atau start hot. Dalam start cold, aplikasi akan dimulai dari awal. Di kondisi yang lain, sistem diharuskan untuk memindah aplikasi yang berjalan dari latar belakang ke latar depan. Sebaiknya selalu lakukan pengoptimalan berdasarkan asumsi start cold. Karena hal tersebut juga dapat meningkatkan performa start warm dan hot.

Untuk mengoptimalkan aplikasi Anda agar startup-nya cepat, memahami apa yang terjadi pada tingkat sistem dan aplikasi, serta bagaimana interaksinya, dalam masing-masing keadaannya sangatlah berguna.

Start cold

Start cold mengacu pada aplikasi yang dimulai dari awal: sampai tahap start ini, proses sistem belum membuat proses aplikasi. Start cold terjadi jika aplikasi Anda diluncurkan untuk pertama kalinya sejak perangkat di-booting, atau sejak sistem menutup aplikasi. Jenis start ini menghadirkan tantangan terbesar dalam hal meminimalkan waktu startup, karena sistem dan aplikasi memiliki pekerjaan yang lebih banyak daripada kondisi peluncuran lainnya.

Di awal start cold, sistem memiliki tiga tugas. Tugas tersebut adalah:

  1. Memuat dan meluncurkan aplikasi.
  2. Menampilkan jendela awal kosong untuk aplikasi segera setelah peluncuran.
  3. Membuat proses aplikasi.

Segera setelah sistem membuat proses aplikasi, proses aplikasi bertanggung jawab untuk tahap selanjutnya:

  1. Membuat objek aplikasi.
  2. Meluncurkan thread utama.
  3. Membuat aktivitas utama.
  4. Meluaskan tampilan.
  5. Menata layar.
  6. Menjalankan penggambaran awal.

Setelah proses aplikasi menyelesaikan penggambaran awal, proses sistem menukar jendela latar belakang yang saat ini ditampilkan, menggantinya dengan aktivitas utama. Pada tahap ini, pengguna dapat mulai menggunakan aplikasi.

Gambar 1 menunjukkan bagaimana sistem dan proses aplikasi saling berbagi tugas satu sama lain.


Gambar 1. Representasi visual bagian-bagian penting dari peluncuran aplikasi secara cold.

Masalah performa dapat muncul selama pembuatan aplikasi dan pembuatan aktivitas.

Pembuatan aplikasi

Saat aplikasi diluncurkan, jendela awal kosong tetap muncul di layar sampai sistem selesai menggambar aplikasi untuk pertama kalinya. Pada tahap tersebut, proses sistem menukar jendela awal aplikasi Anda, memungkinkan pengguna mulai berinteraksi dengan aplikasi.

Jika Application.onCreate() di aplikasi Anda kelebihan muatan, sistem akan memanggil metode onCreate() pada objek aplikasi Anda. Setelah itu, aplikasi akan memunculkan thread utama, yang juga dikenal sebagai UI thread, dan menugaskannya untuk membuat aktivitas utama Anda.

Pada tahap ini, proses tingkat aplikasi dan sistem akan memproses sesuai dengan tahap siklus proses aplikasi.

Pembuatan aktivitas

Setelah proses aplikasi membuat aktivitas, aktivitas menjalankan operasi berikut:

  1. Menginisialisasi nilai.
  2. Memanggil konstruktor.
  3. Memanggil metode callback, seperti Activity.onCreate(), sesuai dengan keadaan siklus proses aktivitas saat ini.

Biasanya, metode onCreate() memiliki dampak terbesar pada waktu pemuatan, karena metode tersebut menjalankan pekerjaan dengan overhead tertinggi: memuat dan meng-inflate tampilan, serta menginisialisasi objek yang perlu dijalankan oleh aktivitas.

Start hot

Start hot aplikasi jauh lebih sederhana dan memiliki overhead yang lebih rendah dari start cold. Pada start hot, yang dilakukan semua sistem adalah memindah aktivitas ke latar depan. Jika semua aktivitas aplikasi Anda masih tersimpan di memori, aplikasi tersebut dapat mengabaikan pengulangan inisialisasi objek, peluasan tata letak, dan rendering.

Namun, jika beberapa memori telah dihapus akibat peristiwa pemangkasan memori, seperti onTrimMemory(), objek tersebut perlu dibuat ulang sebagai respons untuk peristiwa start hot.

Start hot menampilkan perilaku on-screen yang sama dengan skenario start cold: Proses sistem menampilkan layar kosong hingga aplikasi selesai merender aktivitas.

Start warm

Start warm mencakup beberapa subset operasi yang berlangsung selama start cold; di waktu yang sama, menunjukkan overhead yang lebih banyak dari start hot. Ada banyak kemungkinan kondisi yang dapat dianggap sebagai start warm. Sebagai contoh:

  • Pengguna keluar dari aplikasi Anda, tetapi kemudian meluncurkannya kembali. Proses ini mungkin akan terus berjalan, tetapi aplikasi harus membuat ulang aktivitas dari awal melalui panggilan ke onCreate().
  • Sistem akan mengeluarkan aplikasi Anda dari memori, lalu pengguna meluncurkannya kembali. Proses dan aktivitas perlu dimulai ulang, tetapi tugas dapat memanfaatkan paket status instance tersimpan yang diteruskan ke onCreate().

Mendeteksi dan mendiagnosis masalah

Android menyediakan beberapa cara untuk memberi tahu Anda bahwa aplikasi Anda bermasalah, serta membantu Anda mendiagnosisnya. Android vitals dapat memberi tahu Anda bahwa masalah terjadi, dan fitur diagnosis dapat membantu Anda mendiagnosis masalah tersebut.

Android vitals

Android vitals dapat membantu meningkatkan performa aplikasi dengan memberi tahu Anda, melalui Konsol Play, jika waktu startup aplikasi Anda berlebihan. Android vitals menganggap waktu startup aplikasi Anda berlebihan jika:

  • Startup cold membutuhkan waktu 5 detik atau lebih lama.
  • Startup warm membutuhkan waktu 2 detik atau lebih lama.
  • Startup hot membutuhkan waktu 1,5 detik atau lebih lama.

Android vitals tidak melaporkan data untuk startup hot. Untuk informasi cara Google Play mengumpulkan data Android vitals, lihat dokumentasi Konsol Play.

Mendiagnosis waktu startup yang lambat

Untuk mendiagnosis performa waktu mulai dengan benar, Anda dapat melacak metrik yang menampilkan berapa lama aplikasi Anda dimulai.

Waktu hingga tampilan awal

Di Android 4.4 (API level 19) dan yang lebih tinggi, logcat menyertakan baris output yang berisi nilai yang disebut Displayed. Nilai ini mewakili jumlah waktu yang berlalu antara meluncurkan proses dan menyelesaikan menggambar aktivitas terkait di layar. Waktu yang berlalu mencakup urutan peristiwa berikut:

  1. Meluncurkan proses.
  2. Menginisialisasi objek.
  3. Membuat dan menginisialisasi aktivitas.
  4. Meluaskan tata letak.
  5. Menggambar aplikasi untuk pertama kalinya.

Baris log yang dilaporkan terlihat mirip dengan contoh berikut:

    ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms
    

Jika Anda melacak output logcat dari command line, atau dalam terminal, Anda dapat menemukan waktu berlalu dengan mudah. Untuk menemukan waktu berlalu di Android Studio, Anda harus menonaktifkan filter dalam tampilan logcat Anda. Menonaktifkan filter diperlukan karena server sistem, bukan aplikasi itu sendiri, menayangkan log ini.

Setelah selesai membuat setelan yang benar, Anda dapat dengan mudah menelusuri istilah yang tepat untuk melihat waktunya. Gambar 2 menunjukkan cara menonaktifkan filter, dan, di baris kedua output dari bawah, adalah contoh output logcat dari waktu Displayed.


Gambar 2. Menonaktifkan filter dan menemukan nilai Displayed di logcat.

Metrik Displayed dalam output logcat tidak selalu merekam jumlah waktu sampai semua resource dimuat dan ditampilkan: metrik ini melewatkan resource yang tidak direferensikan di file tata letak atau file yang dibuat aplikasi sebagai bagian dari inisialisasi objek. Metrik tersebut tidak menyertakan resource ini karena memuatnya merupakan suatu proses inline, dan tidak memblokir tampilan awal aplikasi.

Terkadang baris Displayed dalam output logcat berisi kolom tambahan untuk waktu total. Contoh:

    ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms (total +1m22s643ms)
    

Dalam hal ini, pengukuran pertama kali hanya dilakukan untuk aktivitas yang pertama kali digambar. Pengukuran waktu total dimulai saat proses aplikasi dijalankan, dan dapat menyertakan aktivitas lain yang dimulai terlebih dahulu, tetapi tidak menampilkan apa pun di layar. Pengukuran waktu total hanya ditampilkan ketika ada perbedaan antara aktivitas tunggal dan waktu startup total.

Anda juga dapat mengukur waktu tampilan awal dengan menjalankan aplikasi menggunakan perintah ADB Shell Activity Manager. Berikut contohnya:

adb [-d|-e|-s <serialNumber>] shell am start -S -W
    com.example.app/.MainActivity
    -c android.intent.category.LAUNCHER
    -a android.intent.action.MAIN
Metrik Displayed muncul dalam output logcat seperti sebelumnya. Jendela terminal Anda seharusnya juga menampilkan baris berikut:
Starting: Intent
    Activity: com.example.app/.MainActivity
    ThisTime: 2044
    TotalTime: 2044
    WaitTime: 2054
    Complete
    

Argumen -c dan -a bersifat opsional dan memungkinkan Anda menentukan <category> dan <action> untuk intent.

Waktu hingga tampilan penuh

Anda dapat menggunakan metode reportFullyDrawn() untuk mengukur waktu yang telah berlalu antara peluncuran aplikasi serta tampilan lengkap semua resource dan hierarki tampilan. Hal ini dapat sangat berguna jika suatu aplikasi menjalankan pemuatan lambat. Dalam pemuatan lambat, aplikasi tidak memblokir penggambaran awal jendela, tetapi memuat resource dan mengupdate hierarki tampilannya secara asinkron.

Jika, akibat pemuatan lambat, tampilan awal aplikasi tidak menyertakan semua resource, sebaiknya pertimbangkan untuk memuat sepenuhnya serta menampilkan semua resource dan tampilan sebagai metrik terpisah: Misalnya, UI harus dimuat sepenuhnya, dengan menggambar beberapa teks, tetapi masih belum menampilkan gambar yang harus diambil aplikasi dari jaringan.

Untuk mengatasi masalah ini, Anda dapat memanggil reportFullyDrawn() secara manual untuk memberi tahu sistem bahwa aktivitas Anda sudah menyelesaikan pemuatan lambat. Saat Anda menggunakan metode ini, nilai yang ditampilkan logcat adalah waktu yang telah berlalu dari pembuatan objek aplikasi sampai saat reportFullyDrawn() dipanggil. Berikut adalah contoh dari output logcat:

system_process I/ActivityManager: Fully drawn {package}/.MainActivity: +1s54ms

Output logcat terkadang menyertakan waktu total, seperti yang dibahas dalam Waktu hingga tampilan awal.

Jika mengetahui bahwa waktu tampilan lebih lambat dari yang diinginkan, Anda dapat mencoba mengidentifikasi bottleneck dalam proses startup.

Mengidentifikasi bottleneck

Cara mudah untuk mencari bottleneck adalah dengan CPU Profiler Android Studio. Untuk informasinya, lihat Memeriksa aktivitas CPU dengan CPU Profiler.

Anda juga dapat memperoleh insight tentang potensi bottleneck melalui pelacakan inline di dalam metode onCreate() aplikasi dan aktivitas Anda. Untuk mempelajari pelacakan inline, lihat dokumentasi untuk fungsi Trace, dan ringkasan pelacakan sistem.

Memperhatikan masalah umum

Bagian ini membahas beberapa masalah yang kerap memengaruhi performa startup aplikasi. Masalah ini terutama menyangkut inisialisasi objek aplikasi dan aktivitas, serta pemuatan layar.

Menginisialisasi aplikasi berat

Performa peluncuran dapat terpengaruh jika kode Anda mengganti objek Application, dan mengeksekusi pekerjaan berat atau logika yang kompleks saat menginisialisasi objek tersebut. Aplikasi dapat membuang waktu selama startup jika subclass Aplikasi Anda melakukan inisialisasi yang masih belum perlu dilakukan. Beberapa inisialisasi mungkin sama sekali tidak diperlukan: misalnya, menginisialisasi informasi status untuk aktivitas utama, ketika aplikasi telah benar-benar dimulai oleh suatu intent. Dengan intent, aplikasi hanya menggunakan subset data status yang telah diinisialisasi sebelumnya.

Kesulitan lainnya selama inisialisasi aplikasi mencakup peristiwa pembersihan sampah memori yang paling berdampak atau sangat banyak, atau I/O disk terjadi bersamaan dengan inisialisasi, memblokir proses inisialisasi lebih lanjut. Pembersihan sampah memori terutama pertimbangan dengan waktu proses Dalvik; waktu proses Art yang menjalankan pembersihan sampah memori secara serentak, meminimalkan dampak operasi.

Mendiagnosis masalah

Anda dapat menggunakan metode pelacakan atau pelacakan inline untuk mendiagnosis masalahnya.

Pelacakan metode

Menjalankan CPU Profiler mengungkapkan bahwa metode callApplicationOnCreate() akhirnya memanggil metode com.example.customApplication.onCreate. Jika alat ini menunjukkan bahwa metode ini membutuhkan waktu lama untuk menyelesaikan eksekusi, sebaiknya pelajari lebih lanjut untuk melihat pekerjaan apa yang terjadi di sana.

Pelacakan inline

Gunakan pelacakan inline untuk menyelidiki kemungkinan masalah yang mencakup:

  • Fungsi onCreate() awal aplikasi Anda.
  • Semua objek singleton global yang diinisialisasi aplikasi.
  • Semua I/O disk, deserialisasi, atau loop ketat yang mungkin terjadi selama kendala terjadi.

Solusi untuk masalah tersebut

Terlepas dari apakah masalahnya terletak pada inisialisasi yang tidak perlu atau I/O disk, solusinya adalah dengan memanggil objek yang dimuat dengan lambat: hanya menginisialisasi objek-objek yang dibutuhkan dengan segera. Misalnya, daripada membuat objek statis global, pindahkan ke pola singleton, di mana aplikasi hanya menginisialisasi objek saat pertama kali mengaksesnya. Pertimbangkan juga untuk menggunakan dependensi framework injeksi seperti Dagger yang membuat objek dan dependensi saat melakukan injeksi untuk pertama kalinya.

Menginisialisasi aktivitas berat

Pembuatan aktivitas kerap memerlukan banyak pekerjaan overhead yang tinggi. Sering kali ada peluang untuk mengoptimalkan pekerjaan ini untuk mendapatkan peningkatan performa. Masalah umum tersebut meliputi:

  • Meluaskan tata letak yang besar atau kompleks.
  • Memblokir menggambar layar pada disk, atau I/O jaringan.
  • Memuat dan mendekode bitmap.
  • Meraster objek VectorDrawable.
  • Inisialisasi subsistem lain dari aktivitas.

Mendiagnosis masalah

Dalam hal ini, begitu juga, kedua pelacakan metode dan pelacakan inline terbukti bermanfaat.

Pelacakan metode

Saat menggunakan Profiler CPU, perhatikan konstruktor subclass Application dan metode com.example.customApplication.onCreate() aplikasi Anda.

Jika alat ini menunjukkan bahwa metode ini membutuhkan waktu lama untuk menyelesaikan eksekusi, sebaiknya pelajari lebih lanjut untuk melihat pekerjaan apa yang terjadi di sana.

Pelacakan inline

Gunakan pelacakan inline untuk menyelidiki kemungkinan masalah yang mencakup:

  • Fungsi onCreate() awal aplikasi Anda.
  • Semua objek singleton global yang diinisialisasi olehnya.
  • Semua I/O disk, deserialisasi, atau loop ketat yang mungkin terjadi selama kendala terjadi.

Solusi untuk masalah tersebut

Terdapat banyak potensi kendala, tetapi dua masalah dan solusi umumnya adalah sebagai berikut:

  • Makin besar hierarki tampilan Anda, makin banyak waktu yang dibutuhkan aplikasi untuk meng-inflate-nya. Dua langkah yang dapat Anda lakukan untuk mengatasi masalah ini adalah:
    • Meratakan hierarki tampilan dengan mengurangi tata letak yang berlebihan atau tersarang.
    • Tidak meng-inflate bagian UI yang tidak perlu terlihat selama peluncuran. Sebagai gantinya, gunakan objek ViewStub sebagai placeholder untuk sub-hierarki yang dapat di-inflate aplikasi pada waktu yang lebih tepat.
  • Melakukan semua inisialisasi resource di thread utama juga dapat memperlambat startup. Anda dapat mengatasi masalah ini seperti berikut:
    • Memindahkan semua inisialisasi resource sehingga aplikasi tersebut dapat menjalankannya dengan lambat di thread yang berbeda.
    • Mengizinkan aplikasi memuat dan menampilkan tampilan, dan kemudian mengupdate properti visual yang bergantung pada bitmap dan resource lainnya.

Layar peluncuran bertema

Anda mungkin ingin memberi tema pengalaman pemuatan aplikasi, sehingga layar peluncuran aplikasi konsisten bertema dengan aplikasi lainnya, bukan hanya dengan memberi tema sistem. Hal tersebut dapat menyembunyikan peluncuran aktivitas yang lambat.

Cara umum untuk menerapkan layar peluncuran bertema adalah dengan menggunakan atribut tema windowDisablePreview untuk menonaktifkan layar kosong awal yang digambar oleh proses sistem saat meluncurkan aplikasi. Namun, cara ini dapat menyebabkan waktu startup yang lebih lama dari aplikasi yang tidak menyembunyikan jendela pratinjau. Selain itu, cara tersebut memaksa pengguna menunggu tanpa respons saat aktivitas diluncurkan, membuat pengguna bertanya-tanya apakah aplikasi berfungsi dengan benar atau tidak.

Mendiagnosis masalah

Anda dapat secara berulang mendiagnosis masalah ini dengan mengamati respons yang lambat saat pengguna meluncurkan aplikasi Anda. Dalam hal ini, layar mungkin tampak membeku, atau telah berhenti merespons input.

Solusi untuk masalah tersebut

Daripada menonaktifkan jendela pratinjau, sebaiknya Anda mengikuti pola Desain Material yang umum. Anda dapat menggunakan atribut tema windowBackground aktivitas guna memberikan drawable kustom sederhana untuk aktivitas awal.

Misalnya, Anda dapat membuat file drawable baru dan merujuknya dari XML tata letak dan file manifes aplikasi seperti berikut:

File XML tata letak:

    <layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:opacity="opaque">
      <!-- The background color, preferably the same as your normal theme -->
      <item android:drawable="@android:color/white"/>
      <!-- Your product logo - 144dp color version of your app icon -->
      <item>
        <bitmap
          android:src="@drawable/product_logo_144dp"
          android:gravity="center"/>
      </item>
    </layer-list>
    

File manifes:

    <activity ...
    android:theme="@style/AppTheme.Launcher" />
    

Cara termudah untuk beralih kembali ke tema normal adalah dengan memanggil setTheme(R.style.AppTheme) sebelum memanggil super.onCreate() dan setContentView():

Kotlin

    class MyMainActivity : AppCompatActivity() {

        override fun onCreate(savedInstanceState: Bundle?) {
            // Make sure this is before calling super.onCreate
            setTheme(R.style.Theme_MyApp)
            super.onCreate(savedInstanceState)
            // ...
        }
    }
    

Java

    public class MyMainActivity extends AppCompatActivity {
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        // Make sure this is before calling super.onCreate
        setTheme(R.style.Theme_MyApp);
        super.onCreate(savedInstanceState);
        // ...
      }
    }