Rekomendasi untuk arsitektur Android (Tampilan)

Konsep dan penerapan Jetpack Compose

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

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:

  • Anda harus membuat repositori meskipun hanya berisi satu sumber data.
  • Di aplikasi kecil, Anda dapat memilih untuk menempatkan jenis lapisan data dalam paket atau modul data.

Rekomendasi

Deskripsi

Ikuti Aliran Data Searah (UDF).

Sangat direkomendasikan

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.

Sangat direkomendasikan

Gunakan ViewModels AAC untuk menangani logika bisnis, dan mengambil data aplikasi untuk mengekspos status UI ke UI.

Lihat praktik terbaik ViewModel lainnya di sini.

Lihat manfaat ViewModel di sini.

Gunakan koleksi status UI yang mendukung siklus proses.

Sangat direkomendasikan

Kumpulkan status UI dari UI menggunakan builder coroutine berbasis siklus proses yang sesuai, repeatOnLifecycle.

Baca selengkapnya tentang repeatOnLifecycle.

Jangan mengirim peristiwa dari ViewModel ke UI.

Sangat direkomendasikan

Proses peristiwa secara langsung di ViewModel dan menyebabkan pembaruan status dengan hasil penanganan peristiwa. Selengkapnya tentang peristiwa UI di sini.

Gunakan aplikasi aktivitas tunggal.

Direkomendasikan

Gunakan Navigation Fragment untuk menavigasi antar-layar dan deep link ke aplikasi Anda jika aplikasi memiliki lebih dari satu layar.

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

class MyFragment : Fragment() {

    private val viewModel: MyViewModel by viewModel()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        viewLifecycleOwner.lifecycleScope.launch {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collect {
                    // Process item
                }
            }
        }
    }
}

ViewModel

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

Rekomendasi

Deskripsi

ViewModels seharusnya tidak bergantung pada siklus proses Android.

Sangat direkomendasikan

ViewModel tidak boleh menyimpan referensi ke jenis terkait Siklus Proses. Jangan teruskan Activity, Fragment, Context, atau Resources sebagai dependensi. Jika sesuatu memerlukan Context di ViewModel, Anda harus benar-benar mengevaluasi apakah hal tersebut dilakukan di lapisan yang tepat.

Gunakan coroutine dan flow.

Sangat direkomendasikan

ViewModel berinteraksi dengan lapisan data atau domain menggunakan:

  • Flow Kotlin untuk menerima data aplikasi,
  • Fungsi suspend untuk melakukan tindakan menggunakan viewModelScope.

Menggunakan ViewModel pada tingkat layar.

Sangat direkomendasikan

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

  • Aktivitas/Fragmen dalam Tampilan
  • Tujuan atau grafik saat menggunakan Jetpack Navigation.

Jangan gunakan AndroidViewModel.

Sangat direkomendasikan

Gunakan class ViewModel, bukan AndroidViewModel. Class Application tidak boleh digunakan di ViewModel. Sebagai gantinya, pindahkan dependensi ke UI atau lapisan data.

Ekspos status UI.

Direkomendasikan

ViewModel harus mengekspos data ke UI melalui satu properti yang disebut uiState. Jika UI menampilkan beberapa bagian data yang tidak terkait, ViewModel dapat mengekspos beberapa properti status UI.

  • Anda harus menjadikan uiState sebagai StateFlow.
  • Anda harus membuat uiState menggunakan operator stateIn dengan kebijakan WhileSubscribed(5000) (contoh) jika data muncul sebagai aliran data dari lapisan hierarki lain.
  • 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
            )

    // ...
}

Siklus Proses

Berikut adalah beberapa praktik terbaik untuk menggunakan siklus proses Android:

Rekomendasi

Deskripsi

Jangan mengganti metode siklus proses dalam Activity atau Fragment.

Sangat direkomendasikan

Jangan mengganti metode siklus proses seperti onResume dalam Activity atau Fragment. Sebagai gantinya, gunakan LifecycleObserver. Jika aplikasi perlu melakukan pekerjaan saat siklus proses mencapai Lifecycle.State tertentu, gunakan repeatOnLifecycle API.

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

class MyFragment: Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver {
            override fun onResume(owner: LifecycleOwner) {
                // ...
            }
            override fun onPause(owner: LifecycleOwner) {
                // ...
            }
        }
    }
}