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

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

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

问题

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

override suspend fun getDataStore(
    context: Context,
    fileKey: String
): DataStore<SingleWidgetInfo> {
    val dataStore = context.dataStoreBuilder(
        name = DATA_STORE_FILENAME + fileKey, // Use fileKey to create a unique name
        serializer = WeatherInfoSerializer
    ).build()
    
    return dataStore
}

这样,每次调用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) ?

 /**
 * Use the same file name regardless of the widget instance to share data between them
 *
 * If you need different state/data for each instance, create a store using the provided fileKey
 */
private val Context.datastore by dataStore(DATA_STORE_FILENAME, WeatherInfoSerializer)

override suspend fun getDataStore(
    context: Context,
    fileKey: String
): DataStore&lt;SingleWidgetInfo&gt; {
    return context.datastore // How to use fileKey while creating the datastore ?
}

What I tried failed :

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

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

Thanks

答案1

得分: 1

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

已经搞定了但不太美观`dateStore` 下面的代码不是公开的所以我不得不复制/粘贴/调整了一些代码

object SingleWidgetDataStateDefinition : GlanceStateDefinition<SingleWidgetData> {

    private const val DATA_STORE_FILENAME = "singleWidget"

    //private val Context.datastore by dataStore(DATA_STORE_FILENAME, SingleWidgetDataSerializer)

    override suspend fun getDataStore(
        context: Context,
        fileKey: String
    ): DataStore<SingleWidgetData> {
        return DataStoreSingletonDelegate(
            serializer = SingleWidgetDataSerializer,
            fileName = DATA_STORE_FILENAME + fileKey,
            corruptionHandler = null,
            produceMigrations = { listOf() },
            scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
        ).getValue(context)
    }
}

and

class DataStoreSingletonDelegate<T> internal constructor(
    private val fileName: String,
    private val serializer: Serializer<T>,
    private val corruptionHandler: ReplaceFileCorruptionHandler<T>?,
    private val produceMigrations: (Context) -> List<DataMigration<T>>,
    private val scope: CoroutineScope
) {

    private val lock = Any()

    @GuardedBy("lock")
    @Volatile
    private var INSTANCE: DataStore<T>? = null

    fun getValue(thisRef: Context): DataStore<T> {
        return INSTANCE ?: synchronized(lock) {
            if (INSTANCE == null) {
                val applicationContext = thisRef.applicationContext
                INSTANCE = DataStoreFactory.create(
                    serializer = serializer,
                    produceFile = { applicationContext.dataStoreFile(fileName) },
                    corruptionHandler = corruptionHandler,
                    migrations = produceMigrations(applicationContext),
                    scope = scope
                )
            }
            INSTANCE!!
        }
    }
}

请注意,代码中的注释部分(例如 //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 :

object SingleWidgetDataStateDefinition : GlanceStateDefinition&lt;SingleWidgetData&gt; {

    private const val DATA_STORE_FILENAME = &quot;singleWidget&quot;

    //private val Context.datastore by dataStore(DATA_STORE_FILENAME, SingleWidgetDataSerializer)

    override suspend fun getDataStore(
        context: Context,
        fileKey: String
    ): DataStore&lt;SingleWidgetData&gt; {
        return DataStoreSingletonDelegate(
            serializer = SingleWidgetDataSerializer,
            fileName = DATA_STORE_FILENAME + fileKey,
            corruptionHandler = null,
            produceMigrations = { listOf() },
            scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
        ).getValue(context)
    }
}

and

class DataStoreSingletonDelegate&lt;T&gt; internal constructor(
    private val fileName: String,
    private val serializer: Serializer&lt;T&gt;,
    private val corruptionHandler: ReplaceFileCorruptionHandler&lt;T&gt;?,
    private val produceMigrations: (Context) -&gt; List&lt;DataMigration&lt;T&gt;&gt;,
    private val scope: CoroutineScope
) {

    private val lock = Any()

    @GuardedBy(&quot;lock&quot;)
    @Volatile
    private var INSTANCE: DataStore&lt;T&gt;? = null

    fun getValue(thisRef: Context): DataStore&lt;T&gt; {
        return INSTANCE ?: synchronized(lock) {
            if (INSTANCE == null) {
                val applicationContext = thisRef.applicationContext
                INSTANCE = DataStoreFactory.create(
                    serializer = serializer,
                    produceFile = { applicationContext.dataStoreFile(fileName) },
                    corruptionHandler = corruptionHandler,
                    migrations = produceMigrations(applicationContext),
                    scope = scope
                )
            }
            INSTANCE!!
        }
    }
}

答案2

得分: 1

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

// 为了更清晰,将DATA_STORE_FILENAME重命名为DATA_STORE_FILENAME_PREFIX
private const val DATA_STORE_FILENAME_PREFIX = "weatherInfo_"

// 以下行可以移除,因为不再需要
//private val Context.datastore by dataStore(DATA_STORE_FILENAME, WeatherInfoSerializer)

// getDataStore 现在使用 DataStoreFactory
override suspend fun getDataStore(context: Context, fileKey: String) = DataStoreFactory.create(
    serializer = WeatherInfoSerializer,
    produceFile = { getLocation(context, fileKey) }
)

override fun getLocation(context: Context, fileKey: String) =
    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).

// Rename DATA_STORE_FILENAME for clarity
 private const val DATA_STORE_FILENAME_PREFIX = &quot;weatherInfo_&quot;

// The below line can be removed as it is no longer needed
//private val Context.datastore by dataStore(DATA_STORE_FILENAME, WeatherInfoSerializer)


// getDataStore now uses DataStoreFactory
    override suspend fun getDataStore(context: Context, fileKey: String) = DataStoreFactory.create(
        serializer = WeatherInfoSerializer,
        produceFile = { getLocation(context, fileKey) }
    )

    override fun getLocation(context: Context, fileKey: String) =
        context.dataStoreFile(DATA_STORE_FILENAME_PREFIX + fileKey.lowercase())

答案3

得分: 0

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

override suspend fun getDataStore(
    context: Context,
    fileKey: String
): DataStore<SingleWidgetInfo> {
    val store by dataStore(DATA_STORE_FILENAME + fileKey, WeatherInfoSerializer)
    return store
}
英文:

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:

override suspend fun getDataStore(
    context: Context,
    fileKey: String
): DataStore&lt;SingleWidgetInfo&gt; {
    val store by dataStore(DATA_STORE_FILENAME + fileKey, WeatherInfoSerializer)
    return store
}

</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:

确定