android:datastore
datastore
차이
문서의 선택한 두 판 사이의 차이를 보여줍니다.
양쪽 이전 판이전 판다음 판 | 이전 판 | ||
android:datastore [2025/04/27 20:19] – 이거니맨 | android:datastore [2025/05/01 14:52] (현재) – 데이터스토어로 저장하는 예제 이거니맨 | ||
---|---|---|---|
줄 1: | 줄 1: | ||
- | ===== 개요 ===== | + | ===== 서론 ===== |
+ | |||
+ | |||
+ | |||
+ | 앱과 관련된 데이터를 램이 아닌 낸드와 같은 영구저장장치(보조저장장치)에 저장한 후 이를 재사용하고 싶을 때가 있다. | ||
+ | |||
+ | 이를테면 앱의 테마 색깔을 계속 유지하고 싶다거나, | ||
+ | |||
+ | 이렇게 앱이 종료된 이후에도 계속하여 데이터를 보조저장장치에 저장한 후에 앱이 재사용될 때에 불러오는 것은 세가지 방법을 떠올릴 수 있을 것이다. | ||
+ | |||
+ | |||
+ | ==== 1. 별도의 파일에 데이터를 쓰고 저장하기 ==== | ||
+ | |||
+ | 전통적인 방법은 파일을 직접 제어하여 문자열을 저장한 후에, 이를 다시 불러오는 것이다. | ||
+ | |||
+ | 단, [[android: | ||
+ | |||
+ | 이렇게 파일을 저장할 때에는 [[android: | ||
+ | |||
+ | 참고로 이렇게 별도의 파일로 저장하는 방법으로 오랫동안 사용된 것은 ini파일이다. 베데스다의 폴아웃 프랜차이즈나 엘더스크롤 프랜차이즈는 모두 | ||
+ | |||
+ | 심지어 베데스다 소프트웍스는 가장 최신의 게임인 스타필드마저 게임 설정에 ini파일을 이용하였다. | ||
+ | |||
+ | {{: | ||
+ | |||
+ | |||
+ | ==== 2. 데이터베이스 이용하기 ==== | ||
+ | |||
+ | 데이터베이스를 이용하여 데이터를 영구적으로 저장할 수도 있을 것이다. 안드로이드에서는 [[android: | ||
+ | |||
+ | 그런데 데이터베이스는 레코드가 수없이 많이 쌓이므로 그 중에 어느 레코드를 설정파일로 사용할 것인지 지정해주어야 한다. | ||
+ | |||
+ | 이를테면 isUser라는 필드를 하나 만들어서 ' | ||
+ | |||
+ | 따라서 간단한 설정 저장에 데이터베이스를 쓰는 것은 닭잡는 데에 소잡는 칼을 쓰는 것과 같다. | ||
+ | |||
+ | |||
+ | ==== 3. Datastore | ||
+ | |||
+ | [키-키값] 쌍으로 구성되어 있어서 설정파일만 간단하게 불러오는 것을 안드로이드에서는 Datastore라고 한다. | ||
+ | |||
+ | 구글은 다음과 같이 설명하고 있다. | ||
+ | |||
+ | <WRAP center round tip 95%> | ||
+ | Jetpack Datastore는 [[https:// | ||
+ | </ | ||
+ | |||
+ | |||
+ | 어찌되었건 대충 이해하자면 Android에서 설정 등을 기록하고 불러오는 기능을 말한다. 과거에 shared preference라는 이름으로 제공하던 api가 현재는 [[https:// | ||
- | Android에서 설정 등을 기록하고 불러오는 기능을 말한다. 과거에 shared preference라는 이름으로 제공하던 api가 | ||
- | 현재는 [[https:// | ||
===== 필요한 의존성 파일 ===== | ===== 필요한 의존성 파일 ===== | ||
- | datastore api와, lifecycle api가 필요하다. | + | datastore api와, lifecycle api가 필요하다. |
모듈단계의 그래들 파일에 다음의 의존성을 추가한다. | 모듈단계의 그래들 파일에 다음의 의존성을 추가한다. | ||
+ | |||
<code kotlin> | <code kotlin> | ||
// DataStore | // DataStore | ||
줄 65: | 줄 112: | ||
} | } | ||
</ | </ | ||
+ | |||
+ | ==== 2. 인스턴스 생성하기 ==== | ||
+ | |||
+ | " | ||
+ | |||
+ | <code kotlin> | ||
+ | private val Context.dataStore : DataStore< | ||
+ | </ | ||
+ | |||
+ | ==== 3. 키값의 제목 설정하기 ==== | ||
+ | |||
+ | Datastore는 [키, 밸류]의 쌍으로 데이터를 저장한다. 이를테면 C#의 Dictionary와 매우 비슷한 구조인 것이다. | ||
+ | |||
+ | 즉, 각 키별로 형식이 존재한다. 이를테면 int값을 저장할 때에는 intPreferencesKey라고 하고, boolean값을 저장할 때에는 booleanPreferencesKey라고 한다. 자세한 것은 [[https:// | ||
+ | |||
+ | 우리는 boolean값을 " | ||
+ | |||
+ | <code kotlin> | ||
+ | private val FILTER_ON = booleanPreferencesKey(" | ||
+ | </ | ||
+ | |||
+ | ==== 4. 키 값 읽기 ==== | ||
+ | |||
+ | 키값은 context.datastore.data.map으로 읽는다. | ||
+ | |||
+ | <code kotlin> | ||
+ | // Datastore 읽기 | ||
+ | // Flow : coroutines.flow import 해야됨 | ||
+ | val valueOfFilter : Flow< | ||
+ | .map {preferences -> | ||
+ | preferences[FILTER_ON] ?: false | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | 반환값이 Flow임을 유의하자 | ||
+ | |||
+ | ==== 5. 키 값 쓰기 ==== | ||
+ | |||
+ | context.datastore.edit로 키 값을 쓴다. | ||
+ | |||
+ | <code kotlin> | ||
+ | // Datastore 쓰기 | ||
+ | suspend fun setFilterSwitch(switch : Boolean){ | ||
+ | context.dataStore.edit { preferences-> | ||
+ | preferences[FILTER_ON] = switch | ||
+ | } | ||
+ | } | ||
+ | |||
+ | </ | ||
+ | |||
+ | <WRAP center round tip 90%> | ||
+ | 자동적으로 다음이 임포트 되지 않을 수가 있다. 만약 안드로이드 스튜디오가 컴플레인을 하면 다음을 임포트하자 | ||
+ | |||
+ | import androidx.datastore.preferences.core.edit | ||
+ | </ | ||
+ | |||
+ | |||
===== 싱글턴으로 만들기 ===== | ===== 싱글턴으로 만들기 ===== | ||
+ | 위의 datastore 인스턴스를 싱글톤으로 단 하나만 존재하게 하기 위하여 MainApplication을 다음과 같이 만들어준다. | ||
+ | 다음 예제는 database도 생성하는 코드가 같이 있는 것이니 이를 염두에 두고 살펴보자 | ||
- | ===== 실제 compose내에서 사용하기 | + | <file kotlin " |
+ | import android.annotation.SuppressLint | ||
+ | import android.app.Application | ||
+ | import android.content.Context | ||
+ | import androidx.datastore.core.DataStore | ||
+ | import androidx.datastore.preferences.core.Preferences | ||
+ | import androidx.datastore.preferences.preferencesDataStore | ||
+ | import androidx.room.Room.databaseBuilder | ||
+ | import com.dklaw.memorize.database.DataStoreMemorize | ||
+ | import com.dklaw.memorize.database.ScoreDatabase | ||
+ | |||
+ | class MainApplication : Application() { | ||
+ | |||
+ | init { | ||
+ | instance = this | ||
+ | } | ||
+ | |||
+ | companion object { | ||
+ | private var instance: MainApplication? | ||
+ | |||
+ | fun applicationContext() : Context { | ||
+ | return instance!!.applicationContext | ||
+ | } | ||
+ | |||
+ | lateinit var scoreDatabase: | ||
+ | |||
+ | // Datastore 변수 | ||
+ | @SuppressLint(" | ||
+ | private lateinit var dataStore: DataStoreMemorize | ||
+ | |||
+ | fun getThis() : MainApplication { | ||
+ | return instance!! | ||
+ | } | ||
+ | |||
+ | fun getDateStore() : DataStoreMemorize = dataStore | ||
+ | } | ||
+ | |||
+ | override fun onCreate() { | ||
+ | super.onCreate() | ||
+ | // initialize for any | ||
+ | |||
+ | // Use ApplicationContext. | ||
+ | val context: Context = MainApplication.applicationContext() | ||
+ | |||
+ | // Datastore single Instance | ||
+ | dataStore = DataStoreMemorize(this) | ||
+ | |||
+ | // Database Initialize | ||
+ | scoreDatabase = databaseBuilder( | ||
+ | applicationContext, | ||
+ | ScoreDatabase:: | ||
+ | ScoreDatabase.NAME | ||
+ | ).build() | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== 실제 compose내에서 사용하기 | ||
+ | |||
+ | ==== 1. 읽기 ==== | ||
+ | |||
+ | 다음과 같이 싱글톤으로 불러온 다음 헬퍼클래스의 읽이 함수를 호출한다. | ||
+ | |||
+ | <code kotlin> | ||
+ | // 데이터 스토어 세팅 값 | ||
+ | val isFiltered by MainApplication.getDateStore().valueOfFilter.collectAsState(initial = false) | ||
+ | </ | ||
+ | |||
+ | |||
+ | Flow로 받아오므로 colletAsState로 값을 받아온 것을 알 수 있다. | ||
+ | |||
+ | ==== 2. 쓰기 ==== | ||
+ | |||
+ | 다음과 같이 코루틴을 통해 저장한다. | ||
+ | |||
+ | <code kotlin> | ||
+ | // 스위치 버튼, 코루틴 | ||
+ | @Composable | ||
+ | fun switchButton(modifier: | ||
+ | |||
+ | val coroutineScope = rememberCoroutineScope() | ||
+ | val isFiltered by MainApplication.getDateStore().valueOfFilter.collectAsStateWithLifecycle(initialValue = false) | ||
+ | |||
+ | Row(modifier = modifier, horizontalArrangement = Arrangement.SpaceBetween, | ||
+ | Text(text = if (isFiltered) {" | ||
+ | .padding(vertical = 4.dp) | ||
+ | .weight(0.5f), | ||
+ | Switch( | ||
+ | checked = isFiltered, | ||
+ | onCheckedChange = { | ||
+ | |||
+ | coroutineScope.launch { MainApplication.getDateStore().setFilterSwitch(!isFiltered) } | ||
+ | |||
+ | }, | ||
+ | modifier = Modifier | ||
+ | .weight(0.5f) | ||
+ | .semantics { this.contentDescription = " | ||
+ | thumbContent = if (isFiltered) { | ||
+ | { | ||
+ | Icon( | ||
+ | imageVector = Icons.Filled.Check, | ||
+ | contentDescription = " | ||
+ | modifier = Modifier.size(SwitchDefaults.IconSize), | ||
+ | ) | ||
+ | } | ||
+ | } else { | ||
+ | { | ||
+ | imageVector = Icons.Filled.Close, | ||
+ | contentDescription = " | ||
+ | modifier = Modifier.size(SwitchDefaults.IconSize), | ||
+ | )} | ||
+ | } | ||
+ | ) | ||
+ | } | ||
+ | } | ||
+ | </ |
android/datastore.1745752770.txt.gz · 마지막으로 수정됨: 2025/04/27 20:19 저자 이거니맨