首页 > 编程知识 正文

android组件,android系统四大组件

时间:2023-05-04 20:18:09 阅读:234328 作者:2783

Android Jetpack组件系列文章:
Android Jetpack组件(一)LifeCycle
Android Jetpack组件(二)Navigation
Android Jetpack组件(三)ViewModel
Android Jetpack组件(四)LiveData
Android Jetpack组件(五)Room
Android JetPack组件(六)DataBinding
Android Jetpack组件(七)Paging
Android Jetpack组件(八)WorkManager
Android Jetpack组件(九)DataStore

疫情距离我最近的一次,隔离的第10天,居家办公的第8天,希望疫情早点过去,结束隔离✊。

首语

数据持久化指将哪些内存中的瞬时数据保存到存储设备中,保证即使在手机或电脑关机的情况下,数据依然不会丢失。

Android系统中主要提供了三种方式来实现数据持久化功能。即文件存储、SharedPreferences存储及数据库存储。其中SharedPreferences是使用键值对的方式来存储轻量型数据,使用比较简单,且程序卸载后也会一并清除,不会残留数据。但是SharedPreferences也存在很多缺点,它是对磁盘进行I/O操作,会引起性能问题,导致ANR,且多线程场景下效率低下、存储延迟,存储较大数据如json或html会频繁引起GC,导致界面卡顿,曾经在项目开发中使用SharedPreferences碰到数据缓存延迟的情况,后面就使用了腾讯的MMKV。

现在,Google推出DataStore,旨在代替SharedPreferences,克服其大部分缺点。

Jetpack DataStore 是一种数据存储解决方案,允许您使用协议缓冲区存储键值对或类型化对象。DataStore 使用 Kotlin 协程和 Flow 以异步、一致的事务方式存储数据。

对比

DataStore 提供两种不同的实现:Preferences DataStore 和 Proto DataStore。

Preferences DataStore 由类 DataStore 和 Preferences 实现,使用键存储和访问数据。此实现不需要预定义的架构,也不确保类型安全。Proto DataStore 将数据作为自定义数据类型的实例序列化存储在磁盘。此实现要求您使用协议缓冲区(Protocol Buffers)来定义架构,但可以确保类型安全。

Protocol Buffers (ProtocolBuffer/ protobuf )是Google公司开发的一种数据描述语言,类似于XML能够将结构化数据序列化,可用于数据存储、通信协议等方面。同XML相比,Protocol buffers在序列化结构化数据方面有许多优点:

更简单。

数据描述文件只需原来的1/10至1/3。

解析速度是原来的20倍至100倍。

减少了二义性。

生成了更容易在编程中使用的数据访问类。

下图是Google对SharedPreferences与DataStore两种不同实现的对比。

依赖 // Preferences DataStoreimplementation "androidx.datastore:datastore-preferences:1.0.0"// Proto DataStoreimplementation "androidx.datastore:datastore-core:1.0.0" 使用

在两种实现中,除非另外特指,否则 DataStore 会将首选项存储在文件中,并且所有的数据操作都会在 Dispatchers.IO 上执行。

Preferences DataStore 创建

使用由PreferencesDataStore创建的属性委托来创建 Datastore<Preferences> 实例。在 kotlin 文件顶层调用该实例一次,便可在应用的所有其余部分通过此属性访问该实例。这样可以更轻松地将 DataStore 保留为单例。

val dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings") 读取

由于 Preferences DataStore 不使用预定义的架构,因此您必须使用相应的键类型函数为需要存储在 DataStore<Preferences> 实例中的每个值定义一个键。例如,如需为 int 值定义一个键,请使用 intPreferencesKey()。。然后,使用 DataStore.data 属性读取内容。

val USER_INFO = intPreferencesKey("user_info")dataStore.data.collect {val i = it.toPreferences()[USER_INFO] tvContent.text=i.toString()} 写入

Preferences DataStore 提供了一个edit() 函数,用于以事务方式更新 DataStore 中的数据。该函数的 transform 参数接受代码块,您可以在其中根据需要更新值。转换块中的所有代码均被视为单个事务,查看源码可知。

dataStore.edit { settings -> val currentCounterValue = settings[USER_INFO] ?: 0 settings[USER_INFO] = currentCounterValue + 1 } Proto DataStore

Proto DataStore 实现使用 DataStore 和协议缓冲区将类型化的对象保留在磁盘上。

导入 导入plugins 插件。在app的build.gradle中添加如下代码。 plugins { id "com.android.application" id "kotlin-android" id "com.google.protobuf" version "0.8.12"} 添加依赖。 implementation "com.google.protobuf:protobuf-javalite:3.10.0" 配置 protoc 命令,与dependencies同级。 protobuf { protoc { // //从仓库下载 protoc 这里的版本号需要与依赖 com.google.protobuf:protobuf-javalite:xxx 版本相同 artifact = 'com.google.protobuf:protoc:3.10.0' } generateProtoTasks { all().each { task -> task.builtins { java { option "lite" } } } } // 默认生成目录 $buildDir/generated/source/proto 通过 generatedFilesBaseDir 改变生成位置 generatedFilesBaseDir = "$projectDir/src/main"} 设置proto文件位置。 android { sourceSets { main { proto { // proto 文件默认路径是 src/main/proto // 可以通过 srcDir 修改 proto 文件的位置 srcDir 'src/main/proto' } } }} 编译项目。

在app/src/main目录下新建一个文件夹proto,然后在文件夹proto下新建一个.proto类型的文件UserPrefs,编写proto文件及其字段,重新构建项目。

// 固定的,还有proto2syntax="proto3";// 格式:包名 + . + 文件名option java_package = "com.yhj.kotlincomponent.protobuf";//可以生成单独的.java每个生成的类的文件option java_multiple_files = true;message Settings { int32 count = 1;}

这里推荐安装 Protocol Buffer Editor插件,它为协议缓冲区文件提供编辑器支持。语法高亮、编辑器增强功能等有点,调试起来非常方便。

对于proto3语法,使用技巧,参考Google proto3教程,讲解详细。

构建完成后,就可以看到appsrcmaindebug目录下生成了protobuf文件目录,里面包含Settings、SettingsOrBuilder、UserPrefs文件。

创建

定义一个实现 Serializer<T> 的类,其中 T 是 proto 文件中定义的类型。此序列化器类会告知 DataStore 如何读取和写入您的数据类型。请务必为该序列化器添加默认值,以便在尚未创建任何文件时使用。

使用由 dataStore 创建的属性委托来创建 DataStore<T> 的实例,其中 T 是在 proto 文件中定义的类型。在您的 Kotlin 文件顶层调用该实例一次,便可在应用的所有其余部分通过此属性委托访问该实例。filename 参数会告知 DataStore 使用哪个文件存储数据,而 serializer 参数会告知 DataStore 上面中定义的序列化器类的名称。

object SettingsSerializer : Serializer<Settings> { override val defaultValue: Settings = Settings.getDefaultInstance() override suspend fun readFrom(input: InputStream): Settings { try { return Settings.parseFrom(input) } catch (exception: InvalidProtocolBufferException) { throw CorruptionException("Cannot read proto.", exception) } } override suspend fun writeTo( t: Settings, output: OutputStream ) = t.writeTo(output)}val Context.settingsDataStore: DataStore<Settings> by dataStore( fileName = "settings.pb", serializer = SettingsSerializer) 写入

Proto DataStore 提供了一个updateData() 函数,用于以事务方式更新存储的对象。updateData() 为您提供数据的当前状态,作为数据类型的一个实例,并在原子读-写-修改操作中以事务方式更新数据。

settingsDataStore.updateData { currentSettings -> currentSettings.toBuilder() .setCount(currentSettings.count + 1) .build() } 读取

使用 DataStore.data来获取存储的数据。

settingsDataStore.data.collect { Log.e("yhj", it.count.toString(), )} 迁移SharedPreferences

在创建DataStore时,preferencesDataStore参数里包含produceMigrations参数,用来迁移SharedPreferences,需要执行一次读取或者写入操作,DataStore才会自动合并,迁移成功后会删除原有的SharedPreferences文件,Proto DataStore 用法相同。

val dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings", produceMigrations = { con -> listOf(SharedPreferencesMigration(con, "app_cache")) }) 原理

使用由PreferencesDataStore创建的属性委托来创建 Datastore<Preferences> 实例。

override fun getValue(thisRef: Context, property: KProperty<*>): DataStore<Preferences> { return INSTANCE ?: synchronized(lock) { if (INSTANCE == null) { val applicationContext = thisRef.applicationContext INSTANCE = PreferenceDataStoreFactory.create( corruptionHandler = corruptionHandler, migrations = produceMigrations(applicationContext), scope = scope ) { applicationContext.preferencesDataStoreFile(name) } } INSTANCE!! } }

创建了一个文件用于将键值对写入磁盘,文件位于applicationContext.filesDir+datastore/的子目录中。文件后缀名为.preferences_pb,

public fun Context.preferencesDataStoreFile(name: String): File = this.dataStoreFile("$name.preferences_pb")public fun Context.dataStoreFile(fileName: String): File = File(applicationContext.filesDir, "datastore/$fileName")

数据的读取直接通过dataStore.data获取,数据的写入通过dataStore.edit,实际上也是通过dataStore.updateData来写入的

public interface DataStore<T> { public val data: Flow<T> public suspend fun updateData(transform: suspend (t: T) -> T): T} public suspend fun DataStore<Preferences>.edit( transform: suspend (MutablePreferences) -> Unit): Preferences { return this.updateData { // It's safe to return MutablePreferences since we freeze it in // PreferencesDataStore.updateData() it.toMutablePreferences().apply { transform(this) } }} 总结

DataStore的两种实现相比而言,Preferences DataStore相对简单一些,Proto DataStore比较复杂些。

DataStore克服了SharedPreference的许多缺点,Google也大力推荐,所以是时候跟SharedPreference说再见了,拥抱 Jetpack DataStore。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。