1. Sebelum memulai
Pengantar
Di unit ini, Anda telah mempelajari cara menggunakan SQL dan Room untuk menyimpan data secara lokal di perangkat. SQL dan Room adalah alat yang canggih. Namun, jika Anda tidak memerlukan penyimpanan data relasional, DataStore dapat memberikan solusi sederhana. Komponen DataStore Jetpack adalah cara yang bagus untuk menyimpan set data kecil dan sederhana dengan overhead rendah. DataStore memiliki dua implementasi yang berbeda, yaitu Preferences DataStore dan Proto DataStore.
Preferences DataStoremenyimpan key-value pair. Nilainya dapat berupa jenis data dasar Kotlin, sepertiString,Boolean, danInteger. Database ini tidak menyimpan set data yang kompleks. Tidak memerlukan skema yang telah ditetapkan sebelumnya. Kasus penggunaan utamaPreferences Datastoreadalah untuk menyimpan preferensi pengguna di perangkatnya.Proto DataStoremenyimpan jenis data kustom. Implementasi ini memerlukan skema yang telah ditentukan sebelumnya dan memetakan definisi proto dengan struktur objek.
Hanya Preferences DataStore yang dibahas dalam codelab ini, tetapi Anda dapat membaca Proto DataStore lebih lanjut di dokumentasi DataStore.
Preferences DataStore adalah cara yang tepat untuk menyimpan setelan yang dikontrol pengguna. Dalam codelab ini, Anda akan mempelajari cara menerapkan DataStore untuk melakukannya.
Prasyarat:
- Menyelesaikan kursus Dasar-Dasar Android dengan Compose melalui codelab Membaca dan Memperbarui Data dengan Room.
Yang akan Anda butuhkan
- Komputer yang memiliki akses internet dan Android Studio
- Perangkat atau emulator
- Kode awal untuk aplikasi Dessert Release
Yang akan Anda build
Aplikasi Dessert Release menampilkan daftar rilis Android. Ikon di panel aplikasi mengalihkan tata letak antara tampilan petak dan tampilan daftar.

Dalam status saat ini, aplikasi tidak mempertahankan pemilihan tata letak. Saat Anda menutup aplikasi, pilihan tata letak tidak akan disimpan dan setelan akan kembali ke pilihan default. Dalam codelab ini, Anda akan menambahkan DataStore ke aplikasi Dessert Release dan menggunakannya untuk menyimpan preferensi pemilihan tata letak.
2. Mendownload kode awal
Klik link berikut guna mendownload semua kode untuk codelab ini:
Atau jika mau, Anda dapat meng-clone kode Dessert Release dari GitHub:
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-dessert-release.git $ cd basic-android-kotlin-compose-training-dessert-release $ git checkout starter
- Di Android Studio, buka folder
basic-android-kotlin-compose-training-dessert-release. - Buka kode aplikasi Dessert Release di Android Studio.
3. Menyiapkan dependensi
Tambahkan kode berikut ke dependencies di file app/build.gradle.kts:
implementation("androidx.datastore:datastore-preferences:1.0.0")
4. Mengimplementasikan repositori preferensi pengguna
- Pada paket
data, buat class baru bernamaUserPreferencesRepository.

- Di konstruktor
UserPreferencesRepository, tentukan properti nilai pribadi untuk mewakili instance objekDataStoredengan jenisPreferences.
class UserPreferencesRepository(
private val dataStore: DataStore<Preferences>
){
}
DataStore menyimpan key-value pair. Untuk mengakses nilai, Anda harus menentukan kunci.
- Buat
companion objectdi dalam classUserPreferencesRepository. - Gunakan fungsi
booleanPreferencesKey()untuk menentukan kunci dan teruskan namais_linear_layout. Serupa dengan nama tabel SQL, kunci perlu menggunakan format garis bawah. Kunci ini digunakan untuk mengakses nilai boolean yang menunjukkan apakah tata letak linear harus ditampilkan.
class UserPreferencesRepository(
private val dataStore: DataStore<Preferences>
){
private companion object {
val IS_LINEAR_LAYOUT = booleanPreferencesKey("is_linear_layout")
}
...
}
Menulis ke DataStore
Anda membuat dan mengubah nilai dalam DataStore dengan meneruskan lambda ke metode edit(). Lambda meneruskan instance MutablePreferences, yang dapat Anda gunakan untuk memperbarui nilai di DataStore. Semua update di dalam lambda ini dijalankan sebagai satu transaksi. Dengan kata lain, update bersifat atomik — semuanya terjadi dalam satu waktu. Jenis update ini mencegah situasi saat beberapa nilai diperbarui, sedangkan yang lainnya tidak.
- Buat fungsi penangguhan, lalu beri nama
saveLayoutPreference(). - Dalam fungsi
saveLayoutPreference(), panggil metodeedit()pada objekdataStore.
suspend fun saveLayoutPreference(isLinearLayout: Boolean) {
dataStore.edit {
}
}
- Agar kode lebih mudah dibaca, tentukan nama untuk
MutablePreferencesyang disediakan di isi lambda. Gunakan properti tersebut untuk menyetel nilai dengan kunci yang Anda tentukan dan boolean akan diteruskan ke fungsisaveLayoutPreference().
suspend fun saveLayoutPreference(isLinearLayout: Boolean) {
dataStore.edit { preferences ->
preferences[IS_LINEAR_LAYOUT] = isLinearLayout
}
}
Membaca dari DataStore
Setelah Anda membuat cara untuk menulis isLinearLayout ke dalam dataStore, lakukan langkah-langkah berikut untuk membacanya:
- Buat properti di
UserPreferencesRepositorydari jenisFlow<Boolean>yang disebutisLinearLayout.
val isLinearLayout: Flow<Boolean> =
- Anda dapat menggunakan properti
DataStore.datauntuk menampilkan nilaiDataStore. SetelisLinearLayoutke propertidataobjekDataStore.
val isLinearLayout: Flow<Boolean> = dataStore.data
Properti data adalah Flow dari objek Preferences. Objek Preferences berisi semua key-value pair di DataStore. Setiap kali data di DataStore diperbarui, objek Preferences baru akan dimunculkan ke dalam Flow.
- Gunakan fungsi peta untuk mengonversi
Flow<Preferences>menjadiFlow<Boolean>.
Fungsi ini menerima lambda dengan objek Preferences saat ini sebagai parameter. Anda dapat menetapkan kunci yang telah ditentukan sebelumnya untuk mendapatkan preferensi tata letak. Perlu diingat bahwa nilai mungkin tidak ada jika saveLayoutPreference belum dipanggil, jadi Anda juga harus menyediakan nilai default.
- Tentukan
trueuntuk menyetel default ke tampilan tata letak linear.
val isLinearLayout: Flow<Boolean> = dataStore.data.map { preferences ->
preferences[IS_LINEAR_LAYOUT] ?: true
}
Penanganan pengecualian
Setiap kali Anda berinteraksi dengan sistem file di perangkat, mungkin saja ada yang gagal. Misalnya, file mungkin tidak ada, atau disk penuh atau dilepas. Saat DataStore membaca dan menulis data dari file, IOExceptions dapat terjadi saat mengakses DataStore. Anda menggunakan operator catch{} untuk menangkap pengecualian dan menangani kegagalan ini.
- Di objek pendamping, terapkan properti string
TAGyang tidak dapat diubah dan digunakan untuk logging.
private companion object {
val IS_LINEAR_LAYOUT = booleanPreferencesKey("is_linear_layout")
const val TAG = "UserPreferencesRepo"
}
Preferences DataStoremenampilkanIOExceptionketika terjadi error saat membaca data. Di blok inisialisasiisLinearLayout, sebelummap(), gunakan operatorcatch{}untuk menangkapIOException.
val isLinearLayout: Flow<Boolean> = dataStore.data
.catch {}
.map { preferences ->
preferences[IS_LINEAR_LAYOUT] ?: true
}
- Di blok catch, jika ada
IOexception, catat error dan buatemptyPreferences(). Jika jenis pengecualian lain ditampilkan, sebaiknya tampilkan kembali pengecualian tersebut. Dengan memunculkanemptyPreferences()saat terjadi error, fungsi peta masih dapat dipetakan ke nilai default.
val isLinearLayout: Flow<Boolean> = dataStore.data
.catch {
if(it is IOException) {
Log.e(TAG, "Error reading preferences.", it)
emit(emptyPreferences())
} else {
throw it
}
}
.map { preferences ->
preferences[IS_LINEAR_LAYOUT] ?: true
}
5. Melakukan inisialisasi DataStore
Dalam codelab ini, Anda harus menangani injeksi dependensi secara manual. Oleh karena itu, Anda harus menyediakan class UserPreferencesRepository dengan Preferences DataStore secara manual. Ikuti langkah-langkah berikut untuk memasukkan DataStore ke dalam UserPreferencesRepository.
- Temukan paket
dessertrelease. - Dalam direktori ini, buat class baru bernama
DessertReleaseApplicationdan implementasikan classApplication. Ini adalah container untuk DataStore Anda.
class DessertReleaseApplication: Application() {
}
- Di dalam file
DessertReleaseApplication.kt, tetapi di luar classDessertReleaseApplication, deklarasikanprivate const valyang disebutLAYOUT_PREFERENCE_NAME. - Tetapkan variabel
LAYOUT_PREFERENCE_NAMEnilai stringlayout_preferences, yang kemudian dapat Anda gunakan sebagai namaPreferences Datastoreyang Anda buat instance-nya di langkah berikutnya.
private const val LAYOUT_PREFERENCE_NAME = "layout_preferences"
- Masih berada di luar isi class
DessertReleaseApplication, tetapi dalam fileDessertReleaseApplication.kt, buat properti nilai pribadi jenisDataStore<Preferences>yang disebutContext.dataStoremenggunakan delegasipreferencesDataStore. TeruskanLAYOUT_PREFERENCE_NAMEuntuk parameternamedari delegasipreferencesDataStore.
private const val LAYOUT_PREFERENCE_NAME = "layout_preferences"
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
name = LAYOUT_PREFERENCE_NAME
)
- Di dalam isi class
DessertReleaseApplication, buat instancelateinit vardariUserPreferencesRepository.
private const val LAYOUT_PREFERENCE_NAME = "layout_preferences"
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
name = LAYOUT_PREFERENCE_NAME
)
class DessertReleaseApplication: Application() {
lateinit var userPreferencesRepository: UserPreferencesRepository
}
- Ganti metode
onCreate().
private const val LAYOUT_PREFERENCE_NAME = "layout_preferences"
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
name = LAYOUT_PREFERENCE_NAME
)
class DessertReleaseApplication: Application() {
lateinit var userPreferencesRepository: UserPreferencesRepository
override fun onCreate() {
super.onCreate()
}
}
- Di dalam metode
onCreate(), lakukan inisialisasiuserPreferencesRepositorydengan membuatUserPreferencesRepositorydengandataStoresebagai parameternya.
private const val LAYOUT_PREFERENCE_NAME = "layout_preferences"
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
name = LAYOUT_PREFERENCE_NAME
)
class DessertReleaseApplication: Application() {
lateinit var userPreferencesRepository: UserPreferencesRepository
override fun onCreate() {
super.onCreate()
userPreferencesRepository = UserPreferencesRepository(dataStore)
}
}
- Tambahkan baris berikut di dalam tag
<application>dalam fileAndroidManifest.xml.
<application
android:name=".DessertReleaseApplication"
...
</application>
Pendekatan ini menentukan class DessertReleaseApplication sebagai titik entri aplikasi. Tujuan kode ini adalah untuk melakukan inisialisasi dependensi yang ditentukan di class DessertReleaseApplication sebelum meluncurkan MainActivity.
6. Menggunakan UserPreferencesRepository
Menyediakan repositori untuk ViewModel
Setelah UserPreferencesRepository tersedia melalui injeksi dependensi, Anda dapat menggunakannya di DessertReleaseViewModel.
- Di
DessertReleaseViewModel, buat propertiUserPreferencesRepositorysebagai parameter konstruktor.
class DessertReleaseViewModel(
private val userPreferencesRepository: UserPreferencesRepository
) : ViewModel() {
...
}
- Dalam objek pendamping
ViewModel, di blokviewModelFactory initializer, dapatkan instanceDessertReleaseApplicationmenggunakan kode berikut.
...
companion object {
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer {
val application = (this[APPLICATION_KEY] as DessertReleaseApplication)
...
}
}
}
}
- Buat instance
DessertReleaseViewModeldan teruskanuserPreferencesRepository.
...
companion object {
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer {
val application = (this[APPLICATION_KEY] as DessertReleaseApplication)
DessertReleaseViewModel(application.userPreferencesRepository)
}
}
}
}
UserPreferencesRepository kini dapat diakses oleh ViewModel. Langkah berikutnya adalah menggunakan kemampuan baca dan tulis UserPreferencesRepository yang Anda implementasikan sebelumnya.
Menyimpan preferensi tata letak
- Edit fungsi
selectLayout()diDessertReleaseViewModeluntuk mengakses repositori preferensi dan memperbarui preferensi tata letak. - Ingatlah bahwa penulisan ke
DataStoredilakukan secara asinkron dengan fungsisuspend. Mulai Coroutine baru untuk memanggil fungsisaveLayoutPreference()repositori preferensi.
fun selectLayout(isLinearLayout: Boolean) {
viewModelScope.launch {
userPreferencesRepository.saveLayoutPreference(isLinearLayout)
}
}
Membaca preferensi tata letak
Di bagian ini, Anda akan memfaktorkan ulang uiState: StateFlow yang ada di ViewModel untuk mencerminkan isLinearLayout: Flow dari repositori.
- Hapus kode yang melakukan inisialisasi properti
uiStatekeMutableStateFlow(DessertReleaseUiState).
val uiState: StateFlow<DessertReleaseUiState> =
Preferensi tata letak linear dari repositori memiliki dua kemungkinan nilai, yaitu benar atau salah, dalam bentuk Flow<Boolean>. Nilai ini harus dipetakan ke status UI.
- Setel
StateFlowke hasil transformasi koleksimap()yang dipanggil diisLinearLayout Flow.
val uiState: StateFlow<DessertReleaseUiState> =
userPreferencesRepository.isLinearLayout.map { isLinearLayout ->
}
- Tampilkan instance class data
DessertReleaseUiState, dengan meneruskanisLinearLayout Boolean. Layar menggunakan status UI ini untuk menentukan string dan ikon yang benar untuk ditampilkan.
val uiState: StateFlow<DessertReleaseUiState> =
userPreferencesRepository.isLinearLayout.map { isLinearLayout ->
DessertReleaseUiState(isLinearLayout)
}
UserPreferencesRepository.isLinearLayout adalah Flow yang bersifat cold. Namun, untuk memberikan status ke UI, sebaiknya gunakan hot flow, seperti StateFlow, sehingga status selalu tersedia langsung ke UI.
- Gunakan fungsi
stateIn()untuk mengonversiFlowmenjadiStateFlow. - Fungsi
stateIn()menerima tiga parameter:scope,started, daninitialValue. TeruskanviewModelScope,SharingStarted.WhileSubscribed(5_000), danDessertReleaseUiState()untuk parameter ini.
val uiState: StateFlow<DessertReleaseUiState> =
userPreferencesRepository.isLinearLayout.map { isLinearLayout ->
DessertReleaseUiState(isLinearLayout)
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = DessertReleaseUiState()
)
- Luncurkan aplikasi. Perhatikan bahwa Anda dapat mengklik ikon beralih untuk beralih antara tata letak petak dan tata letak linear.

Selamat! Anda berhasil menambahkan Preferences DataStore ke aplikasi untuk menyimpan preferensi tata letak pengguna.
7. Mendapatkan kode solusi
Untuk mendownload kode codelab yang sudah selesai, Anda dapat menggunakan perintah git berikut:
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-dessert-release.git $ cd basic-android-kotlin-compose-training-dessert-release $ git checkout main
Atau, Anda dapat mendownload repositori sebagai file ZIP, lalu mengekstraknya, dan membukanya di Android Studio.
Jika Anda ingin melihat kode solusi, lihat di GitHub.