일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- room
- lifecycle
- hilt
- 알림
- 안드로이드
- CollapsingToolbarLayout
- View
- 코틀린
- CustomView
- HTTP
- 백준
- Coroutine
- Behavior
- Algorithm
- Android
- LiveData
- CoordinatorLayout
- notification
- recyclerview
- AppBarLayout
- activity
- 알고리즘
- DataBinding
- onLayout
- Navigation
- kotlin
- ViewModel
- sqlite
- onMeasure
- BOJ
- Today
- Total
개발일지
Android in A..Z - DataStore 본문
DataStore
Jetpack DataStore는 SharedPreferences를 대체하기 위해 나왔다.
DataStore는 Key-Value뿐만 아니라, Protocol Buffers를 이용하여 Object를 저장할 수 있고, 동기/비동기를 지원하며 IO에 대한 Exception도 제공하기 때문에 유연하게 코드를 작성할 수 있다.
* Android Developer에서 복잡한 구조의 데이터는 Room을 사용해서 저장하는 것을 권장한다.
SharedPreferences 단점
Key-Value 형태로 XML파일에 값을 저장하고 String 또는 Primitives 값만 저장할 수 있다.
=> 이를 해결하고자 JSON이나 XML형식의 String으로 저장하지만 Type Safety를 보장하지 않기 때문에 파싱 과정에서 Exception이 발생할 수 있다.
get() 함수로 동기식으로 데이터를 읽지만 UI Thread를 막기 때문에 ANR을 발생 시킬 수 있다.
apply() 함수로 비동기식으로 데이터를 저장하지만 성공/실패 여부를 확인할 수 없다.
commit() 함수로 동기식으로 데이터를 저장하지만 UI Thread를 막기 때문에 ANR을 발생 시킬 수 있다.
Dependencies
// Typed DataStore (Typed API surface, such as Proto)
dependencies {
implementation "androidx.datastore:datastore:1.0.0-alpha06"
// optional - RxJava2 support
implementation "androidx.datastore:datastore-rxjava2:1.0.0-alpha06"
// optional - RxJava3 support
implementation "androidx.datastore:datastore-rxjava3:1.0.0-alpha06"
}
// Alternatively - use the following artifact without an Android dependency.
dependencies {
implementation "androidx.datastore:datastore-core:1.0.0-alpha06"
}
Preferences DataStore
SharedPreferences처럼 Key-Value구조로 Data를 저장하는 방법이다.
DataStore 생성
최상위 파일에서 선언을 하여 간편하게 접근할 수 있고, 싱글톤 패턴을 유지할수 있습니다.
val Context.loginStore: DataStore<Preferences> by preferencesDataStore(name = "login")
Preferences DataStore 값 읽기/쓰기
stringPreferencesKey, intPreferencesKey 등으로 Key를 만든다.
dataStore.data로 값을 읽는다. (Flow형으로 반환된다.)
dataStore.edit으로 값을 저장한다. (edit이 suspend함수이기 때문에 Coroutine에서 실행해야 한다.)
@Singleton
class LoginRepository @Inject constructor() {
private val idKey by lazy { stringPreferencesKey("id") }
private val passwordKey by lazy { stringPreferencesKey("password") }
@Inject
@LoginDataStore
lateinit var dataStore: DataStore<Preferences>
var id: String
get() {
return runBlocking(Dispatchers.IO) {
dataStore.data.map {
it[idKey] ?: ""
}.first()
}
}
set(value) {
runBlocking(Dispatchers.IO) {
dataStore.edit {
it[idKey] = value
}
}
}
var password: String
get() {
return runBlocking(Dispatchers.IO) {
dataStore.data.map {
it[passwordKey] ?: ""
}.first()
}
}
set(value) {
runBlocking(Dispatchers.IO) {
dataStore.edit {
it[passwordKey] = value
}
}
}
}
Proto DataStore
Protocol Buffers를 이용하여 Object를 저장할 수 있다.
(Protocol Buffers : Data를 직렬화하는 구글의 메커니즘이다. 언어, 플랫폼에 독립적이고 확장이 쉬운 장점이있다.)
Dependency
plugins {
id 'com.google.protobuf' version "0.8.12"
}
dependencies {
implementation "com.google.protobuf:protobuf-javalite:3.11.0"
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.11.0"
}
generateProtoTasks {
all().each { task ->
task.builtins {
java {
option 'lite'
}
}
}
}
}
Protocol Buffers 정의 (정의방법)
프로젝트 app/src/main/proto 에 *.proto파일로 정의해서 저장하고 Rebuild한다.
syntax = "proto3";
option java_package = "com.taetae98.datastore";
option java_multiple_files = true;
message Account {
string department = 1;
string studentId = 2;
string name = 3;
string phone = 4;
}
Serializer 정의
Protocol Buffers를 정의하면 자동으로 객체와 함수들이 생긴다.
object AccountSerializer : Serializer<Account> {
override val defaultValue: Account
get() = Account.getDefaultInstance()
override suspend fun readFrom(input: InputStream): Account {
return try {
Account.parseFrom(input)
} catch (e: Exception) {
throw CorruptionException("Cannot read proto.", e)
}
}
override suspend fun writeTo(t: Account, output: OutputStream) {
t.writeTo(output)
}
}
DataStore 생성
val Context.accountStore: DataStore<Account> by dataStore(
fileName = "user_prefs.pb",
serializer = AccountSerializer
)
DataStore 값 읽기/쓰기
Preferences DataStore처럼 DataStore.data를 통해 값을 읽을 수 있고, Flow형으로 반환된다.
updateData로 값을 쓸 수 있고, suspend 함수이기 때문에 Coroutine을 활용해야 한다.
@Singleton
class AccountRepository @Inject constructor() {
@Inject
lateinit var accountDataStore: DataStore<Account>
val studentId: Flow<String>
get() {
return accountDataStore.data.map { it.studentId }
}
suspend fun set(information: Information) {
accountDataStore.updateData {
it.toBuilder()
.setDepartment(information.department)
.setName(information.name)
.setPhone(information.phone)
.setStudentId(information.studentId)
.build()
}
}
}
Git (예제코드)
github.com/KangTaeJong98/Example/tree/main/Android/DataStore
'Android (안드로이드)' 카테고리의 다른 글
Android in A..Z - OnTouchListener, GestureEvent (0) | 2021.07.18 |
---|---|
Android in A..Z - ActivityResultContract (0) | 2021.05.23 |
Android in A..Z - Dialog (0) | 2021.03.22 |
Android in A..Z - QR Code (0) | 2021.03.16 |
Android in A..Z - Location (0) | 2021.02.06 |