환영 기본 설정 DataStore 🚀

개발자 여러분, 이 기사에서는 DataStore라는 Jetpack 라이브러리 🚀의 새로운 구성원에 대해 설명하겠습니다.

우리는 Android 앱에 일부 데이터를 로컬로 저장하기 위해 오랫동안 공유 기본 설정에 대해 작업하고 있으며 공유 기본 설정이 사용 사례를 지원하는 데 매우 유용하다는 것을 알고 있습니다.

우리의 삶을 더 쉽게 만들기 위해 Android는 더 효율적인 방식으로 앱에서 공유 기본 설정을 구현하기 위해 DataStore를 도입했습니다.

DataStore는 두 가지 유형의 구현을 제공합니다. 기본 설정 DataStore 및 Proto DataStore. 오늘 우리는 Preferences DataStore를 보고 있습니다.

So, why should we use Preferences DataStore? 😕

  • It is an Async API that can be used via Flow
  • It is safe to call on UI thread since work is moved to Dispatchers.IO under the hood
  • It can signal errors
  • It is safe from runtime exceptions
  • It has a transactional API with strong consistency
  • It handles data migration from Shared Preferences


이 친구는 몇 가지 장점이 있습니다. 😃

이제 바로 코드로 들어가 봅시다 💻

For this example I have used Dagger-Hilt to provide dependency Injection.



다음과 같이 build.gradle 파일을 업데이트합시다.

...
apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'

android {

   ...

    // Compile options
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = JavaVersion.VERSION_1_8.toString()
    }

}

dependencies {
    ....

    // Preferences DataStore
    implementation "androidx.datastore:datastore-preferences:1.0.0-alpha01"

    // Lifecycle components
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
    implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
    implementation "androidx.lifecycle:lifecycle-common-java8:2.2.0"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"

    // Kotlin coroutines components
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72"
    api "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9"
    api "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9"

    //Dagger - Hilt
    implementation "com.google.dagger:hilt-android:2.28-alpha"
    kapt "com.google.dagger:hilt-android-compiler:2.28-alpha"

}



// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {

   ....

    dependencies {
        classpath "com.android.tools.build:gradle:4.0.1"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "com.google.dagger:hilt-android-gradle-plugin:2.28-alpha"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}


이제 다음과 같이 Application 클래스 DataStore를 생성합니다.

@HiltAndroidApp
class DataStore: Application() {

    override fun onCreate() {
        super.onCreate()
    }

}


다음으로 모든 로직이 들어갈 DataManager 클래스를 생성할 것입니다. 이 예에서는 사용자 이름, GitHub 사용자 이름 및 즐겨찾기 번호를 저장했습니다.

class DataManager(context: Context) {

    private val dataStore = context.createDataStore("data_prefs")

    companion object{
        val USER_NAME = preferencesKey<String>("USER_NAME")
        val USER_GITHUB = preferencesKey<String>("USER_GITHUB")
        val USER_NO = preferencesKey<Int>("USER_NO")
    }

    suspend fun storeData(name:String, github:String, no:Int){
        dataStore.edit {
            it[USER_NAME] = name
            it[USER_GITHUB] = github
            it[USER_NO] = no
        }
    }

    val userNameFlow:Flow<String> = dataStore.data.map {
        it[USER_NAME] ?: ""
    }

    val userGithubFlow:Flow<String> = dataStore.data.map {
        it[USER_GITHUB] ?: ""
    }

    val userNo: Flow<Int> = dataStore.data.map {
        it[USER_NO] ?: -1
    }

}


여기서 storeData 함수는 비동기로 만들고 우리는 코루틴을 사랑하기 때문에 일시 중단됩니다💛

다음으로 이 DataManager를 제공할 AppModule을 생성합니다.

@Module
@InstallIn(ApplicationComponent::class)
object AppModule {

    @Singleton
    @Provides
    fun provideDataManager(@ApplicationContext context: Context) = DataManager(context)

}


이것은 우리 애플리케이션에 DataManager의 싱글톤을 제공할 것입니다.

다음과 같이 주요 활동에 대한 레이아웃을 만들 차례입니다.

<?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=".MainActivity"
    android:background="#0E0E0E">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="100dp"
        android:fontFamily="@font/poppins_medium"
        android:text="Data Store Example"
        android:textColor="#fff"
        android:textSize="20sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:id="@+id/tvTitle"/>

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/tvTitle"
        android:backgroundTint="@color/colorPrimary"
        android:textColor="#fff"
        android:hint="Enter name"
        android:textColorHint="#8C8C8C"
        android:fontFamily="@font/poppins_medium"
        android:layout_marginTop="50dp"
        android:layout_marginStart="30dp"
        android:layout_marginEnd="30dp"
        android:id="@+id/etName"/>

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/etName"
        android:backgroundTint="@color/colorPrimary"
        android:textColor="#fff"
        android:hint="Github username"
        android:textColorHint="#8C8C8C"
        android:fontFamily="@font/poppins_medium"
        android:layout_marginTop="20dp"
        android:layout_marginStart="30dp"
        android:layout_marginEnd="30dp"
        android:id="@+id/etgithub"/>

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/etgithub"
        android:backgroundTint="@color/colorPrimary"
        android:textColor="#fff"
        android:hint="Enter favourite Number"
        android:textColorHint="#8C8C8C"
        android:fontFamily="@font/poppins_medium"
        android:layout_marginTop="20dp"
        android:layout_marginStart="30dp"
        android:layout_marginEnd="30dp"
        android:id="@+id/etNumber"/>

    <com.google.android.material.button.MaterialButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/etNumber"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="20dp"
        android:backgroundTint="@color/colorPrimary"
        app:cornerRadius="25dp"
        android:text="SAVE"
        android:paddingStart="20dp"
        android:paddingEnd="20dp"
        android:textColor="#fff"
        android:id="@+id/btnSave"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/btnSave"
        app:layout_constraintStart_toStartOf="parent"
        android:text="No data saved to display!"
        android:textColor="#fff"
        android:textSize="16sp"
        android:inputType="number"
        android:id="@+id/tvName"
        android:visibility="gone"
        android:fontFamily="@font/poppins_medium"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="30dp"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/tvName"
        app:layout_constraintStart_toStartOf="parent"
        android:text="No data saved to display!"
        android:textColor="#fff"
        android:textSize="16sp"
        android:visibility="gone"
        android:id="@+id/tvGithub"
        android:fontFamily="@font/poppins_medium"
        app:layout_constraintEnd_toEndOf="parent"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/tvGithub"
        app:layout_constraintStart_toStartOf="parent"
        android:text="No data saved to display!"
        android:textColor="#fff"
        android:textSize="16sp"
        android:id="@+id/tvNo"
        android:visibility="gone"
        android:fontFamily="@font/poppins_medium"
        app:layout_constraintEnd_toEndOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>


드디어 MainActivity가 옵니다 😌

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var dataManager: DataManager
    private var name:String = ""
    private var github:String = ""
    private var no:Int = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        subscribeToObservers()

        btnSave.setOnClickListener {
            val name:String = etName.text.toString()
            val github:String = etgithub.text.toString()
            val no:Int = etNumber.text.toString().toInt()

            if (name.isNotEmpty() && github.isNotEmpty() && no!=-1){
                GlobalScope.launch {
                    dataManager.storeData(name,github,no)
                }
            }else{
                Toast.makeText(this, "Enter details carefully!", Toast.LENGTH_SHORT).show()
            }

        }

    }

    private fun subscribeToObservers() {

        dataManager.userNameFlow.asLiveData().observe(this) {
            name = it
            if (name.isNotEmpty()){
                tvName.visibility = View.VISIBLE
                tvName.text = "Name: $name"
            }
        }

        dataManager.userGithubFlow.asLiveData().observe(this) {
            github = it
            if (github.isNotEmpty()){
                tvGithub.visibility = View.VISIBLE
                tvGithub.text = "Github: $github"
            }
        }

        dataManager.userNo.asLiveData().observe(this) {
            no = it
            if (no != -1){
                tvNo.visibility = View.VISIBLE
                tvNo.text = "Number: $no"
            }
        }

    }
}


여기에서 기본 설정 키를 LiveData로 관찰하고 표시하도록 했습니다.

이 구현에서는 모든 것이 매우 매끄럽고 간단합니다. 😌

전체 코드 참조를 확인하려면 다음을 확인하십시오repository.

다음 이야기에서 만나요..

좋은 웹페이지 즐겨찾기