Journal Entry

Mengatasi UI State yang hilang akibat Process Death di Android | by Anaf Naufalian

Diposting pada Sep 15, 2023 oleh Anaf Naufalian

androidjetpack compose

Mengatasi UI State yang hilang akibat Process Death di Android | by Anaf Naufalian

Anaf Naufalian

Sep 15, 2023

Photo by charlesdeluvio on Unsplash

Process Death, siapa si yang nggak kesel sama masalah ini?, misalnya kita lagi buka aplikasi chatting dan kita udah ketik pesan panjang lebar, pas kita pindah ke aplikasi lain, misalnya ke aplikasi edit video, dan kita mengedit video di aplikasi tersebut, setelah selesai mengedit video kita pindah ke aplikasi chatting tadi, eh tiba-tiba pesan yang kita ketik panjang lebar tadi hilang, nahhh bisa jadi itu karena process death.

Oke, apa itu process death?, Process Death atau yang biasa disebut System-initiated process death adalah kejadian yang terjadi karena sistem Android menghentikan suatu proses dari aplikasi yang sedang berjalan di background (latar belakang) untuk membebaskan sumber daya/RAM yang nantinya akan dipakai oleh aplikasi yang sedang berjalan di foreground (latar depan).

Perbedaan User-initiated process death dan System-initiated process death adalah User-initiated process death prosesnya di kill oleh user, seperti saat mengklik home button. System-initiated process death prosesnya di kill oleh sistem, contohnya seperti perubahan konfigurasi

Seperti contoh diatas, saat kita membuka aplikasi chatting dan kita mengetik pesan di EditText/TextField, biasanya pesan atau state dari EditText/TextField tersebut disimpan di dalam memori (RAM), ketika kita berpindah dari aplikasi chatting ke aplikasi edit video, maka aplikasi chatting beralih ke mode background dan aplikasi edit video beralih ke mode foreground. Saat kita mengedit video pastinya kita membutuhkan lebih banyak sumber daya, ketika sumber daya tidak cukup disinilah sistem Android akan mematikan/kill aplikasi-aplikasi yang berjalan di background, tetapi sebelum sistem Android mematikan aplikasi yang berjalan di background, sistem akan memanggil fungsi onSavedInstanceState() untuk menyimpan state yang kita berikan, setelah fungsi tersebut dipanggil barulah sistem akan mematikan aplikasi tersebut. Nah, setelah edit video tadi kan kita buka lagi aplikasi chatting, tapi kenapa teks yang sudah kita ketik tadi bisa hilang?, itu mungkin karena developer dari aplikasi tersebut tidak mengimplementasikan onSavedInstanceState() untuk menyimpan state.

Ada beberapa cara untuk menyimpan state di android, yaitu:

Untuk kasus ini saya akan mengimplementasikannya di ViewModel dan menggunakan library hilt untuk dependency injection-nya, oke langsung saja pertama kita buat kelas BaseViewModel, untuk apa class BaseViewModel? kelas ini nantinya akan digunakan sebagai parent class dari view model yang akan kita buat, kelas ini juga akan mewarisi (extends) kelas ViewModel.

BaseViewModel.kt

/**
 *  kelas dasar (base class) untuk view model.
 *  Ini berarti kelas ini memberikan kerangka dasar untuk view model
 *  yang akan diturunkan (derived) oleh kelas-kelas lain.
 *
 *  @param savedStateHandle savedStateHandle yang digunakan untuk menyimpan state
 *  @param defaultState default state
 *
 *  @author kafri8889
 */
abstract class BaseViewModel<STATE: Parcelable>(
    private val savedStateHandle: SavedStateHandle,
    private val defaultState: STATE
): ViewModel() {
    // Key yang digunakan untuk menyimpan dan mengambil state di savedStateHandle
    private val KEY_STATE = "state"
    val state: StateFlow<STATE> = savedStateHandle.getStateFlow(KEY_STATE, defaultState)
    /**
     * Function yang digunakan untuk memperbarui state dari [savedStateHandle]
     *
     * @param newState parameter ini akan memberikan state saat ini sebagai `this`.
     */
    protected fun updateState(newState: STATE.() -> STATE) {
        // get current state
        savedStateHandle.get<STATE>(KEY_STATE)?.let { state ->
            // simpan state baru ke savedStateHandle
            savedStateHandle[KEY_STATE] = newState(state)
        }
    }
}

Dari kode diatas BaseViewModel membutuhkan 2 parameter yaitu savedStateHandle dan defaultState.

Kata kunci “STATE” dari kode diatas adalah sebuah generic type yang menggambarkan tipe data dari state yang akan digunakan didalam kelas BaseViewModel,STATE” yang diberikan juga harus mewarisi kelas Parcelable, kenapa harus menggunakan Parcelable?, karena state yang kita berikan adalah kustom data class, dan savedStateHandle menggunakan Bundle untuk menyimpan state yang diberikan, jika kita tidak mengimplementasikan Parcelable ke state yang kita punya dan kita tetap memaksa untuk menyimpan state ke dalam savedStateHandle, maka akan terjadi error.

Selanjutnya kita membuat kelas MyState yang digunakan sebagai tipe data state dalam kelas BaseViewModel.

MyState.kt

@Parcelize
data class MyState(
    val text: String = ""
): Parcelable

Setelah itu kita akan membuat kelas _MyViewModel (_kelas ini harus mewarisi kelas BaseViewModel).

MyViewModel.kt

@HiltViewModel
class MyViewModel @Inject constructor(
    savedStateHandle: SavedStateHandle
): BaseViewModel<MyState>(
    savedStateHandle = savedStateHandle,
    defaultState = MyState()
) {
    /**
     * Funtion yang digunakan untuk memperbarui teks dari text field ke [savedStateHandle]
     */
    fun updateText(newText: String) {
        updateState {
            copy(
                text = newText
            )
        }
    }
}

Selanjutnya kita akan membuat UI yang akan ditampilkan ke pengguna

MyScreen.kt

@Composable
fun MyScreen(
    viewModel: MyViewModel = hiltViewModel()
) {
    // Observe state
    val state by viewModel.state.collectAsStateWithLifecycle()
    Box(
        contentAlignment = Alignment.Center,
        modifier = Modifier
            .fillMaxSize()
    ) {
        OutlinedTextField(
            value = state.text,
            onValueChange = viewModel::updateText
        )
    }
}

Lalu di MainActivity.

MainActivity.kt

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ComposeResearchTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    MyScreen()
                }
            }
        }
    }
}

Jika semuanya sudah selesai, saat kita menjalankan aplikasi harusnya muncul tampilan seperti dibawah

Preview aplikasi

Untuk mensimulasikan System-initiated process death ikuti cara dibawah:

  • Pertama: Run aplikasi di android studio.

Run aplikasi di android studio

  • Kedua: Minimize aplikasi dengan cara menekan tombol “home”.

[other]Minimize aplikasi[/other]

  • Ketiga: Buka “Device Explorer” di android studio dan pindah ke tab “Processes”, disini kita akan melihat package name aplikasi kita jika aplikasi berjalan.

captionless image

  • Keempat: Pilih aplikasi kalian dan tekan tombol “Kill process”.

[other]Kill app from device exploler[/other]

  • Kelima: Buka kembali aplikasi, jika saat membuka aplikasi muncul splash screen atau blank itu tandanya aplikasi sudah di kill sebelumnya, dan jika sebelumnya kalian mengetik teks ke dalam text field sebelum aplikasi di minimize, maka setelah kalian kill aplikasi tadi dan kalian kembali ke aplikasi, seharusnya text yang kalian ketik tadi masih ada.

[other]Preview[/other]

Jika saat kalian mencoba dan hasilnya seperti video diatas maka implementasinya sudah benar, untuk source kodenya bisa klik link dibawah.

[Github-kafri8889/Compose-Research:Processdeath

Contribute to kafri8889/Compose-Research development by creating an account on GitHub.

github.com](https://github.com/kafri8889/Compose-Research/tree/master/app/src/main/java/com/anafthdev/composeresearch/research/base?source=post_page-----d4b0b6c026da---------------------------------------)

Contoh penggunaanya bisa kalian lihat di projek ini

[GitHub - kafri8889/DailyCost: Aplikasi pengelola keuangan

Aplikasi pengelola keuangan. Contribute to kafri8889/DailyCost development by creating an account on GitHub.

github.com](https://github.com/kafri8889/DailyCost?source=post_page-----d4b0b6c026da---------------------------------------)

terima kasih sudah membaca artikel ini.

Referensi