Rekomendasi untuk arsitektur Android

Halaman ini menyajikan beberapa praktik terbaik dan rekomendasi Arsitektur. Gunakan keduanya untuk meningkatkan kualitas, keandalan, dan skalabilitas aplikasi Anda. Alat ini juga memudahkan pemeliharaan dan pengujian aplikasi Anda.

Praktik terbaik di bawah ini dikelompokkan berdasarkan topik. Masing-masing memiliki prioritas yang mencerminkan seberapa kuat rekomendasi tersebut. Daftar prioritasnya adalah sebagai berikut:

  • Sangat direkomendasikan: Terapkan praktik ini kecuali jika pada dasarnya bertentangan dengan pendekatan Anda.
  • Direkomendasikan: Praktik ini kemungkinan akan meningkatkan aplikasi Anda.
  • Opsional: Praktik ini dapat meningkatkan aplikasi Anda dalam keadaan tertentu.

Arsitektur berlapis

Arsitektur berlapis yang kami rekomendasikan mendukung pemisahan fokus. Arsitektur ini mendorong UI dari model data, mematuhi prinsip satu sumber tepercaya, dan mengikuti prinsip aliran data searah. Berikut adalah beberapa praktik terbaik untuk arsitektur berlapis:

Rekomendasi Deskripsi
Gunakan lapisan data yang ditetapkan dengan jelas. Lapisan data mengekspos data aplikasi ke seluruh aplikasi dan berisi sebagian besar logika bisnis aplikasi Anda.
  • Buat repositori meskipun hanya berisi satu sumber data.
  • Di aplikasi kecil, Anda dapat memilih untuk menempatkan jenis lapisan data dalam paket atau modul data.
Gunakan lapisan UI yang jelas. Lapisan UI menampilkan data aplikasi di layar dan berfungsi sebagai titik utama interaksi pengguna. Jetpack Compose adalah toolkit modern yang direkomendasikan untuk membangun UI aplikasi Anda.
  • Di aplikasi kecil, Anda dapat memilih untuk menempatkan jenis lapisan data dalam paket atau modul ui.
Untuk mengetahui informasi selengkapnya tentang praktik terbaik lapisan UI, lihat Lapisan UI.
Mengekspos data aplikasi dari lapisan data menggunakan repositori.

Pastikan komponen di lapisan UI seperti composable atau ViewModel tidak berinteraksi langsung dengan sumber data. Contoh sumber data meliputi:

  • Database, DataStore, SharedPreferences, Firebase API.
  • Penyedia lokasi GPS.
  • Penyedia data Bluetooth.
  • Penyedia status konektivitas jaringan.
Gunakan coroutine dan flow. Gunakan coroutine dan flow untuk berkomunikasi antarlapisan.

Untuk mengetahui informasi selengkapnya tentang praktik terbaik coroutine, lihat Praktik terbaik untuk coroutine di Android.

Menggunakan lapisan domain. Gunakan lapisan domain dengan kasus penggunaan jika Anda perlu menggunakan kembali logika bisnis yang berinteraksi dengan lapisan data di beberapa ViewModel, atau ingin menyederhanakan kompleksitas logika bisnis ViewModel tertentu

Lapisan UI

Peran lapisan UI adalah menampilkan data aplikasi di layar dan berfungsi sebagai titik utama interaksi pengguna. Berikut adalah beberapa praktik terbaik untuk lapisan UI:

Rekomendasi Deskripsi
Ikuti Aliran Data Searah (UDF). Ikuti prinsip-prinsip Aliran Data Searah (UDF), dengan ViewModels mengekspos status UI menggunakan pola observer dan menerima tindakan dari UI melalui panggilan metode.
Gunakan ViewModels AAC jika manfaatnya berlaku untuk aplikasi Anda. Gunakan ViewModel AAC untuk menangani logika bisnis, dan mengambil data aplikasi untuk mengekspos status UI ke UI.

Untuk mengetahui informasi selengkapnya tentang praktik terbaik ViewModel, lihat Rekomendasi arsitektur.

Untuk mengetahui informasi selengkapnya tentang manfaat ViewModel, lihat ViewModel sebagai holder status logika bisnis.

Gunakan koleksi status UI yang mendukung siklus proses. Kumpulkan status UI dari UI menggunakan builder coroutine berbasis siklus proses yang sesuai, collectAsStateWithLifecycle.

Baca selengkapnya tentang collectAsStateWithLifecycle.

Jangan mengirim peristiwa dari ViewModel ke UI. Proses peristiwa secara langsung di ViewModel dan menyebabkan pembaruan status dengan hasil penanganan peristiwa. Untuk mengetahui informasi selengkapnya tentang peristiwa UI, lihat Menangani peristiwa ViewModel.
Gunakan aplikasi aktivitas tunggal. Gunakan Navigation 3 untuk menavigasi antar-layar dan deep link ke aplikasi Anda jika aplikasi memiliki lebih dari satu layar.
Gunakan Jetpack Compose. Gunakan Jetpack Compose untuk mem-build aplikasi baru untuk ponsel, tablet, perangkat foldable, dan Wear OS.

Cuplikan berikut menguraikan cara mengumpulkan status UI dengan cara yang memperhitungkan siklus proses:

  @Composable
  fun MyScreen(
      viewModel: MyViewModel = viewModel()
  ) {
      val uiState by viewModel.uiState.collectAsStateWithLifecycle()
  }

ViewModel

ViewModel bertanggung jawab untuk menyediakan status UI dan mengakses lapisan data. Berikut adalah beberapa praktik terbaik untuk ViewModels:

Rekomendasi Deskripsi
Memastikan ViewModel tidak bergantung pada siklus proses Android. Di ViewModel, jangan menyimpan referensi ke jenis terkait siklus proses. Jangan teruskan Activity, Context, atau Resources sebagai dependensi. Jika sesuatu memerlukan Context di ViewModel, evaluasi dengan cermat apakah hal tersebut dilakukan di lapisan yang tepat.
Gunakan coroutine dan flow.

ViewModel berinteraksi dengan lapisan data atau domain menggunakan hal berikut:

  • Flow Kotlin untuk menerima data aplikasi
  • Fungsi suspend untuk melakukan tindakan menggunakan viewModelScope
Menggunakan ViewModel pada tingkat layar.

Jangan gunakan ViewModel dalam potongan UI yang dapat digunakan kembali. Anda harus menggunakan ViewModel di:

Untuk composable yang lebih kompleks, atau yang memiliki perilaku dinamis berdasarkan status, gunakan rememberViewModelStoreOwner() untuk mencakup ViewModel secara langsung ke situs panggilan composable.

Gunakan class holder status biasa dalam komponen UI yang dapat digunakan kembali. Gunakan class holder status biasa untuk menangani kompleksitas dalam komponen UI yang dapat digunakan kembali. Dengan melakukan ini, status dapat diangkat dan dikontrol secara eksternal.
Jangan gunakan AndroidViewModel. Gunakan class ViewModel, bukan AndroidViewModel. Jangan gunakan class Application di ViewModel. Sebagai gantinya, pindahkan dependensi ke UI atau lapisan data.
Ekspos status UI. Buat ViewModel Anda mengekspos data ke UI melalui satu properti yang disebut uiState. Jika UI menampilkan beberapa bagian data yang tidak terkait, VM dapat mengekspos beberapa properti status UI.
  • Jadikan uiState sebagai StateFlow.
  • Buat uiState menggunakan operator stateIn dengan kebijakan WhileSubscribed(5000) jika data muncul sebagai aliran data dari lapisan hierarki lain. (Lihat contoh kode ini.)
  • Untuk kasus yang lebih sederhana tanpa aliran data yang berasal dari lapisan data, MutableStateFlow dapat diekspos sebagai StateFlow yang tidak dapat diubah.
  • Anda dapat memilih agar ${Screen}UiState sebagai class data yang dapat berisi data, error, dan sinyal pemuatan. Class ini juga dapat berupa class tertutup jika status yang berbeda bersifat eksklusif.

Cuplikan berikut menguraikan cara mengekspos status UI dari ViewModel:

@HiltViewModel
class BookmarksViewModel @Inject constructor(
    newsRepository: NewsRepository
) : ViewModel() {

    val feedState: StateFlow<NewsFeedUiState> =
        newsRepository
            .getNewsResourcesStream()
            .mapToFeedState(savedNewsResourcesState)
            .stateIn(
                scope = viewModelScope,
                started = SharingStarted.WhileSubscribed(5_000),
                initialValue = NewsFeedUiState.Loading
            )

    // ...
}

Lifecycle

Ikuti praktik terbaik untuk menggunakan siklus proses Aktivitas:

Rekomendasi Deskripsi
Gunakan efek yang mendukung siklus proses dalam composable, bukan mengganti callback siklus proses Activity.

Jangan mengganti metode siklus proses Activity, seperti onResume, untuk menjalankan tugas terkait UI. Sebagai gantinya, gunakan LifecycleEffects Compose atau cakupan coroutine yang mendukung siklus proses:

Cuplikan berikut menguraikan cara menjalankan operasi dengan status Siklus Proses tertentu:

  @Composable
  fun LocationChangedEffect(
    locationManager: LocationManager,
    onLocationChanged: (Location) -> Unit
  ) {
    val currentOnLocationChanged by rememberUpdatedState(onLocationChanged)

    LifecycleStartEffect(locationManager) {
        val listener = LocationListener { newLocation ->
            currentOnLocationChanged(newLocation)
        }

        try {
            locationManager.requestLocationUpdates(
                LocationManager.GPS_PROVIDER,
                1000L,
                1f,
                listener,
            )
        } catch (e: SecurityException) {
            // TODO: Handle missing permissions
        }

        onStopOrDispose {
            locationManager.removeUpdates(listener)
        }
    }
  }

Menangani dependensi

Ikuti praktik terbaik saat mengelola dependensi di antara komponen:

Rekomendasi Deskripsi
Gunakan injeksi dependensi. Gunakan praktik terbaik injeksi dependensi, terutama injeksi konstruktor jika memungkinkan.
Cakupan ke komponen jika diperlukan. Cakupan ke container dependensi saat jenis berisi data yang dapat berubah yang perlu dibagikan atau jenisnya mahal untuk diinisialisasi dan digunakan secara luas di aplikasi.
Gunakan Hilt. Gunakan Hilt atau injeksi dependensi manual di aplikasi sederhana. Gunakan Hilt jika project Anda cukup kompleks—misalnya, jika project tersebut mencakup salah satu hal berikut:
  • Beberapa layar dengan ViewModel
  • Menggunakan WorkManager
  • Memiliki ViewModel yang dicakupkan ke data sebelumnya navigasi

Pengujian

Berikut adalah beberapa praktik terbaik untuk pengujian:

Rekomendasi Deskripsi
Ketahui yang harus diuji.

Kecuali jika project ini sesederhana aplikasi "hello world", uji project tersebut. Setidaknya, sertakan hal berikut:

  • Pengujian unit untuk ViewModel, termasuk Flow
  • Pengujian unit untuk entity lapisan data—yaitu, repositori dan sumber data
  • Pengujian navigasi UI yang berguna sebagai pengujian regresi di CI
  • Pengujian performa menggunakan Macrobenchmark untuk mengukur peluncuran aplikasi dan rendering UI
Lebih suka yang palsu daripada tiruan. Untuk mengetahui informasi selengkapnya tentang penggunaan tiruan, lihat Menggunakan dummy pengujian di Android.
Uji StateFlow. Saat menguji StateFlow, lakukan tindakan berikut:

Untuk mengetahui informasi selengkapnya, lihat Apa yang harus diuji di Android dan Menguji tata letak Compose.

Performa

Berikut adalah beberapa praktik terbaik untuk meningkatkan performa:

Rekomendasi Deskripsi
Gunakan R8 untuk menyusutkan dan mengoptimalkan aplikasi Anda. Aktifkan R8 dalam mode penuh di konfigurasi build Anda untuk menghapus kode yang tidak digunakan, meng-obfuscate class, dan menulis ulang kode untuk meningkatkan performa runtime.
Terapkan Profil Dasar Pengukuran dan Profil Startup. Buat Profil Dasar Pengukuran dan Profil Startup untuk mengompilasi jalur kode penting sebelumnya. Hal ini secara signifikan mengurangi latensi startup (TTID dan TTFD) serta meminimalkan jank dan ANR.
Uji performa dengan Macrobenchmark. Gunakan library Macrobenchmark untuk menguji performa aplikasi Anda dan mencegah regresi.

Model

Perhatikan praktik terbaik berikut saat mengembangkan model di aplikasi Anda:

Rekomendasi Deskripsi
Buat model per lapisan di aplikasi yang kompleks.

Dalam aplikasi yang kompleks, buat model baru di berbagai lapisan atau komponen jika memungkinkan. Perhatikan contoh berikut:

  • Sumber data jarak jauh dapat memetakan model yang diterimanya melalui jaringan ke class yang lebih sederhana hanya dengan data yang dibutuhkan aplikasi.
  • Repositori dapat memetakan model DAO ke class data yang lebih sederhana hanya dengan informasi yang diperlukan lapisan UI.
  • ViewModel dapat menyertakan model lapisan data di class UiState.

Konvensi penamaan

Saat memberi nama codebase, Anda harus mengetahui praktik terbaik berikut:

Rekomendasi Deskripsi
Penamaan metode.
Opsional
Gunakan frasa kata kerja untuk memberi nama metode—misalnya, makePayment().
Penamaan properti.
Opsional
Gunakan frasa nomina untuk memberi nama properti—misalnya, inProgressTopicSelection.
Penamaan aliran data.
Opsional
Saat class mengekspos aliran data Flow atau aliran data lainnya, konvensi penamaannya adalah get{model}Stream—misalnya, getAuthorStream(): Flow<Author>. Jika fungsi menampilkan daftar model, gunakan nama model jamak: getAuthorsStream(): Flow<List<Author>>.
Penamaan implementasi antarmuka.
Opsional
Gunakan nama yang bermakna untuk implementasi antarmuka. Gunakan Default sebagai awalan jika nama yang lebih baik tidak dapat ditemukan. Misalnya, untuk antarmuka NewsRepository, Anda dapat menggunakan OfflineFirstNewsRepository, atau InMemoryNewsRepository. Jika Anda tidak dapat menemukan nama yang bagus, gunakan DefaultNewsRepository. Awali implementasi palsu dengan Fake, seperti di FakeAuthorsRepository.

Referensi lainnya

Untuk mengetahui informasi selengkapnya tentang arsitektur Android, lihat referensi tambahan berikut:

Dokumentasi

Melihat konten