Android DataStore:如何使用动态文件名创建它?

huangapple go评论88阅读模式
英文:

Android DataStore : how to create it with a dynamic filename?

问题

私有字段datastore已经使用了DATA_STORE_FILENAMEWeatherInfoSerializer来创建DataStore。如果您想使用不同的fileKey来创建DataStore,您可以考虑将DATA_STORE_FILENAMEWeatherInfoSerializer作为参数传递给getDataStore方法,并在该方法内部使用fileKey来创建DataStore。以下是一种可能的实现方式:

  1. override suspend fun getDataStore(
  2. context: Context,
  3. fileKey: String
  4. ): DataStore<SingleWidgetInfo> {
  5. val dataStore = context.dataStoreBuilder(
  6. name = DATA_STORE_FILENAME + fileKey, // Use fileKey to create a unique name
  7. serializer = WeatherInfoSerializer
  8. ).build()
  9. return dataStore
  10. }

这样,每次调用getDataStore方法时,将使用不同的fileKey创建一个新的DataStore,从而实现了根据fileKey创建不同的DataStore的目的。

英文:

Based on the following Kotlin code (from Glance AppWidget), how can I inject the fileKey in the dataStore() method (which seems possible due to the comment above) ?

  1. /**
  2. * Use the same file name regardless of the widget instance to share data between them
  3. *
  4. * If you need different state/data for each instance, create a store using the provided fileKey
  5. */
  6. private val Context.datastore by dataStore(DATA_STORE_FILENAME, WeatherInfoSerializer)
  7. override suspend fun getDataStore(
  8. context: Context,
  9. fileKey: String
  10. ): DataStore&lt;SingleWidgetInfo&gt; {
  11. return context.datastore // How to use fileKey while creating the datastore ?
  12. }

What I tried failed :

Android DataStore:如何使用动态文件名创建它?

Android DataStore:如何使用动态文件名创建它?

Thanks

答案1

得分: 1

以下是您提供的代码的翻译部分:

  1. 已经搞定了但不太美观`dateStore` 下面的代码不是公开的所以我不得不复制/粘贴/调整了一些代码
  2. object SingleWidgetDataStateDefinition : GlanceStateDefinition<SingleWidgetData> {
  3. private const val DATA_STORE_FILENAME = "singleWidget"
  4. //private val Context.datastore by dataStore(DATA_STORE_FILENAME, SingleWidgetDataSerializer)
  5. override suspend fun getDataStore(
  6. context: Context,
  7. fileKey: String
  8. ): DataStore<SingleWidgetData> {
  9. return DataStoreSingletonDelegate(
  10. serializer = SingleWidgetDataSerializer,
  11. fileName = DATA_STORE_FILENAME + fileKey,
  12. corruptionHandler = null,
  13. produceMigrations = { listOf() },
  14. scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
  15. ).getValue(context)
  16. }
  17. }
  18. and
  19. class DataStoreSingletonDelegate<T> internal constructor(
  20. private val fileName: String,
  21. private val serializer: Serializer<T>,
  22. private val corruptionHandler: ReplaceFileCorruptionHandler<T>?,
  23. private val produceMigrations: (Context) -> List<DataMigration<T>>,
  24. private val scope: CoroutineScope
  25. ) {
  26. private val lock = Any()
  27. @GuardedBy("lock")
  28. @Volatile
  29. private var INSTANCE: DataStore<T>? = null
  30. fun getValue(thisRef: Context): DataStore<T> {
  31. return INSTANCE ?: synchronized(lock) {
  32. if (INSTANCE == null) {
  33. val applicationContext = thisRef.applicationContext
  34. INSTANCE = DataStoreFactory.create(
  35. serializer = serializer,
  36. produceFile = { applicationContext.dataStoreFile(fileName) },
  37. corruptionHandler = corruptionHandler,
  38. migrations = produceMigrations(applicationContext),
  39. scope = scope
  40. )
  41. }
  42. INSTANCE!!
  43. }
  44. }
  45. }

请注意,代码中的注释部分(例如 //private val Context.datastore by dataStore(DATA_STORE_FILENAME, SingleWidgetDataSerializer))未被翻译。如果您需要进一步的翻译或有其他问题,请随时提出。

英文:

Got it working, but it's not very pretty. Code underneath dateStore was not public, So I had to copy/paste/adapt a bit of code :

  1. object SingleWidgetDataStateDefinition : GlanceStateDefinition&lt;SingleWidgetData&gt; {
  2. private const val DATA_STORE_FILENAME = &quot;singleWidget&quot;
  3. //private val Context.datastore by dataStore(DATA_STORE_FILENAME, SingleWidgetDataSerializer)
  4. override suspend fun getDataStore(
  5. context: Context,
  6. fileKey: String
  7. ): DataStore&lt;SingleWidgetData&gt; {
  8. return DataStoreSingletonDelegate(
  9. serializer = SingleWidgetDataSerializer,
  10. fileName = DATA_STORE_FILENAME + fileKey,
  11. corruptionHandler = null,
  12. produceMigrations = { listOf() },
  13. scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
  14. ).getValue(context)
  15. }
  16. }

and

  1. class DataStoreSingletonDelegate&lt;T&gt; internal constructor(
  2. private val fileName: String,
  3. private val serializer: Serializer&lt;T&gt;,
  4. private val corruptionHandler: ReplaceFileCorruptionHandler&lt;T&gt;?,
  5. private val produceMigrations: (Context) -&gt; List&lt;DataMigration&lt;T&gt;&gt;,
  6. private val scope: CoroutineScope
  7. ) {
  8. private val lock = Any()
  9. @GuardedBy(&quot;lock&quot;)
  10. @Volatile
  11. private var INSTANCE: DataStore&lt;T&gt;? = null
  12. fun getValue(thisRef: Context): DataStore&lt;T&gt; {
  13. return INSTANCE ?: synchronized(lock) {
  14. if (INSTANCE == null) {
  15. val applicationContext = thisRef.applicationContext
  16. INSTANCE = DataStoreFactory.create(
  17. serializer = serializer,
  18. produceFile = { applicationContext.dataStoreFile(fileName) },
  19. corruptionHandler = corruptionHandler,
  20. migrations = produceMigrations(applicationContext),
  21. scope = scope
  22. )
  23. }
  24. INSTANCE!!
  25. }
  26. }
  27. }

答案2

得分: 1

我能够从 Kotlin Slack 群组中的 Glance 频道得到帮助。以下更改非常简单,可以直接添加到示例 WeatherApp 中,并且仅在工作器中更改一两行(以便每个小部件显示不同的信息以进行测试)。

  1. // 为了更清晰,将DATA_STORE_FILENAME重命名为DATA_STORE_FILENAME_PREFIX
  2. private const val DATA_STORE_FILENAME_PREFIX = "weatherInfo_"
  3. // 以下行可以移除,因为不再需要
  4. //private val Context.datastore by dataStore(DATA_STORE_FILENAME, WeatherInfoSerializer)
  5. // getDataStore 现在使用 DataStoreFactory
  6. override suspend fun getDataStore(context: Context, fileKey: String) = DataStoreFactory.create(
  7. serializer = WeatherInfoSerializer,
  8. produceFile = { getLocation(context, fileKey) }
  9. )
  10. override fun getLocation(context: Context, fileKey: String) =
  11. context.dataStoreFile(DATA_STORE_FILENAME_PREFIX + fileKey.lowercase())
英文:

I was able to get help from the Kotlin slack group which has a Glance channel. The change below is quite simple and is able to be added directly to the example WeatherApp and only changes a line or two in the worker (so that each widget displays different information for testing purposes).

  1. // Rename DATA_STORE_FILENAME for clarity
  2. private const val DATA_STORE_FILENAME_PREFIX = &quot;weatherInfo_&quot;
  3. // The below line can be removed as it is no longer needed
  4. //private val Context.datastore by dataStore(DATA_STORE_FILENAME, WeatherInfoSerializer)
  5. // getDataStore now uses DataStoreFactory
  6. override suspend fun getDataStore(context: Context, fileKey: String) = DataStoreFactory.create(
  7. serializer = WeatherInfoSerializer,
  8. produceFile = { getLocation(context, fileKey) }
  9. )
  10. override fun getLocation(context: Context, fileKey: String) =
  11. context.dataStoreFile(DATA_STORE_FILENAME_PREFIX + fileKey.lowercase())

答案3

得分: 0

dataStore函数不会返回DataStore本身,而是用于与by关键字一起使用。我不太确定,但我认为这可能会起作用:

  1. override suspend fun getDataStore(
  2. context: Context,
  3. fileKey: String
  4. ): DataStore<SingleWidgetInfo> {
  5. val store by dataStore(DATA_STORE_FILENAME + fileKey, WeatherInfoSerializer)
  6. return store
  7. }
英文:

the dataStore function doesn't return a DataStore itself but is meant to be used together with the by keyword. I'm not entirely sure but I believe this might work:

  1. override suspend fun getDataStore(
  2. context: Context,
  3. fileKey: String
  4. ): DataStore&lt;SingleWidgetInfo&gt; {
  5. val store by dataStore(DATA_STORE_FILENAME + fileKey, WeatherInfoSerializer)
  6. return store
  7. }
  8. </details>

huangapple
  • 本文由 发表于 2023年6月27日 18:03:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/76563756.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定