sharepreferences 의 타입 불안정성과, 메인스레드에서 동작시 런타임오류를 방지하기위해서 나온 기술이라고 한다.
공식문서를 참조 중이였는데 jetpack 라이브러리에서 지원하는 코루틴 Flow 기반의 최신솔루션 기술이라 하니 알아놓는것이 좋을것같다. 이외 참조문헌
아래는 프로젝트에 적용하기위해서 간단하게 테스트코드를 작성하고 적용해보았다.
최종 디테일한 테스트 코드 구조
Nexon API 호출 구조
1.캐릭터명 입력받고 식별자를 얻을수있고 -> api 호출량 1 카운트
2.식별자를 파라미터로 받아 해당하는 정보 를 얻을 수 있음
이러한 구조때문에
캐릭터명 = 식별자를, 키 = 값 형태로 서버 또는 로컬 데이터베이스에
저장해놓게 되면 api 호출시 한번 검색된 키의 값을 재사용 할 수 있어서 사용했다.
ShardPreferences를 사용해도 됬지만, deprecated 되고 공식문서에서 DataStore를 권장하고 있기
때문에 사용해 보았다.
사용하는데 있어서 코루틴의 특성과 Flow에 대해서 어느정도 알 수 있었고 앞으로 자주 사용될것 같아서
관련 개념을 확실하게 이해해야 할 필요성을 느낀다.
package com.android.maplemate.UI
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.emptyPreferences
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import androidx.lifecycle.lifecycleScope
import com.android.maplemate.databinding.ActivityTestBinding
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import java.io.IOException
class TestActivity : AppCompatActivity() {
private val binding by lazy { ActivityTestBinding.inflate(layoutInflater) }
//datastore 객체를 불러옴
private val Context.dataStore:
DataStore<Preferences> by preferencesDataStore(name = "settings")
private val stringKey = "key"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val view = binding.root
setContentView(view)
binding.btnsave.setOnClickListener {
//저장되는 함수
val input = binding.etInputName.text.toString()
lifecycleScope.launch { save(input) }
Toast.makeText(this, "입력받은 텍스트:${input}가 정상적으로 저장되었습니다.", Toast.LENGTH_SHORT).show()
}
binding.btnLoad.setOnClickListener {
//불러오는 함수
val input = binding.etInputName.text.toString()
lifecycleScope.launch { load() }
binding.tvTest.text = "불러온 내용은 :${input} 입니다 "
}
}
//1.저장하는 함수
// 데이터 저장
private suspend fun save(value: String){
val key = stringPreferencesKey(stringKey)
dataStore.edit {
it[key] = value
}
}
//2.불러오는 함수
// 데이터 불러오기
private suspend fun load() : Flow<String> {
val key = stringPreferencesKey(stringKey)
return dataStore.data.catch { e ->
if (e is IOException) {
emit(emptyPreferences())
} else {
throw e
}
}.map {
it[key] ?: "기본 텍스트입니다."
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".UI.TestActivity">
<EditText
android:id="@+id/et_input_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="209dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btnsave"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="160dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="160dp"
android:text="저장"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/et_input_name" />
<Button
android:id="@+id/btn_load"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="불러오기"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btnsave" />
<TextView
android:id="@+id/tv_test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="108dp"
android:layout_marginEnd="92dp"
android:text="입력받은 내용을 불러와 : @@@입니다"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_load" />
</androidx.constraintlayout.widget.ConstraintLayout>
Fragment의 Datastore는 살짝 복잡해서 정리후 포스팅하겠습니다