Skip to content

Commit 87d3cfa

Browse files
committed
fix: handle DataStore preferences corruption gracefully
- Add ReplaceFileCorruptionHandler to automatically recover from corrupted preferences - Create safer access methods with proper error handling - Update all direct DataStore access points to use error-handled methods - Prevent app crashes when preferences proto is corrupted
1 parent 00cbe48 commit 87d3cfa

File tree

4 files changed

+38
-4
lines changed

4 files changed

+38
-4
lines changed

app/src/main/java/com/ismartcoding/plain/MainApp.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import com.ismartcoding.plain.preference.PasswordPreference
2727
import com.ismartcoding.plain.preference.UrlTokenPreference
2828
import com.ismartcoding.plain.preference.WebPreference
2929
import com.ismartcoding.plain.preference.dataStore
30+
import com.ismartcoding.plain.preference.getPreferencesAsync
3031
import com.ismartcoding.plain.receivers.PlugInControlReceiver
3132
import com.ismartcoding.plain.web.HttpServerManager
3233
import com.ismartcoding.plain.workers.FeedFetchWorker
@@ -50,7 +51,7 @@ class MainApp : Application() {
5051
}
5152

5253
coIO {
53-
val preferences = dataStore.data.first()
54+
val preferences = dataStore.getPreferencesAsync()
5455
TempData.webEnabled = WebPreference.get(preferences)
5556
TempData.webHttps = HttpsPreference.get(preferences)
5657
TempData.httpPort = HttpPortPreference.get(preferences)

app/src/main/java/com/ismartcoding/plain/preference/DataStore.kt

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,22 @@ import android.content.Context
44
import androidx.datastore.core.DataStore
55
import androidx.datastore.preferences.core.*
66
import androidx.datastore.preferences.preferencesDataStore
7+
import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
78
import com.ismartcoding.lib.logcat.LogCat
89
import kotlinx.coroutines.flow.catch
910
import kotlinx.coroutines.flow.first
11+
import kotlinx.coroutines.flow.Flow
1012
import java.io.IOException
1113

12-
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
14+
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
15+
name = "settings",
16+
corruptionHandler = ReplaceFileCorruptionHandler(
17+
produceNewData = {
18+
LogCat.e("DataStore preferences corrupted, creating new empty preferences")
19+
emptyPreferences()
20+
}
21+
)
22+
)
1323

1424
suspend fun <T> DataStore<Preferences>.put(
1525
key: Preferences.Key<T>,
@@ -32,3 +42,26 @@ suspend fun <T> DataStore<Preferences>.getAsync(key: Preferences.Key<T>): T? {
3242
}
3343
}.first()[key] as T
3444
}
45+
46+
suspend fun DataStore<Preferences>.getPreferencesAsync(): Preferences {
47+
return data.catch { exception ->
48+
if (exception is IOException) {
49+
LogCat.e("Get data store preferences error $exception")
50+
exception.printStackTrace()
51+
emit(emptyPreferences())
52+
} else {
53+
throw exception
54+
}
55+
}.first()
56+
}
57+
58+
val DataStore<Preferences>.dataFlow: Flow<Preferences>
59+
get() = data.catch { exception ->
60+
if (exception is IOException) {
61+
LogCat.e("Get data store flow error $exception")
62+
exception.printStackTrace()
63+
emit(emptyPreferences())
64+
} else {
65+
throw exception
66+
}
67+
}

app/src/main/java/com/ismartcoding/plain/preference/Settings.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ fun SettingsProvider(content: @Composable () -> Unit) {
6666
)
6767
val settings =
6868
remember {
69-
context.dataStore.data.map {
69+
context.dataStore.dataFlow.map {
7070
Settings(
7171
themeIndex = ThemeIndexPreference.get(it),
7272
customPrimaryColor = CustomPrimaryColorPreference.get(it),

app/src/main/java/com/ismartcoding/plain/preference/WebSettings.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ fun WebSettingsProvider(content: @Composable () -> Unit) {
3838
)
3939
val settings =
4040
remember {
41-
context.dataStore.data.map {
41+
context.dataStore.dataFlow.map {
4242
WebSettings(
4343
passwordType = PasswordTypePreference.get(it),
4444
password = PasswordPreference.get(it),

0 commit comments

Comments
 (0)