The question is specifically for apps targeting API 31 and above.
I have referred to a lot of similar StackOverflow questions, Official Docs, etc.
There are some limitations to API 31 as mentioned here - Docs.
To write a JSON file to the device's external storage so that the data is not lost even if the app is uninstalled.
How to achieve this for apps, targeting API 31 and above?
My current code saves the file to app-specific storage directory obtained using context.getExternalFilesDir(null)
.
The problem is that the file is not retained when the app is uninstalled.
Any blogs or codelabs explaining the exact scenario are also fine.
Thanks to @CommonsWare's comments on my question, I got how to implement it.
My use-case is to create, read and write a JSON file.
I am using Jetpack Compose, so the code shared is for Compose.
Composable code,
val JSON_MIMETYPE = "application/json"
val createDocument = rememberLauncherForActivityResult(
contract = ActivityResultContracts.CreateDocument(
mimeType = JSON_MIMETYPE,
),
) { uri ->
uri?.let {
viewModel.backupDataToDocument(
uri = it,
)
}
}
val openDocument = rememberLauncherForActivityResult(
contract = ActivityResultContracts.OpenDocument(),
) { uri ->
uri?.let {
viewModel.restoreDataFromDocument(
uri = it,
)
}
}
ViewModel code,
fun backupDataToDocument(
uri: Uri,
) {
viewModelScope.launch(
context = Dispatchers.IO,
) {
// Create a "databaseBackupData" custom modal class to write data to the JSON file.
jsonUtil.writeDatabaseBackupDataToFile(
uri = uri,
databaseBackupData = it,
)
}
}
fun restoreDataFromDocument(
uri: Uri,
) {
viewModelScope.launch(
context = Dispatchers.IO,
) {
val databaseBackupData = jsonUtil.readDatabaseBackupDataFromFile(
uri = uri,
)
// Use the fetched data as required
}
}
JsonUtil
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
private val databaseBackupDataJsonAdapter: JsonAdapter<DatabaseBackupData> = moshi.adapter(DatabaseBackupData::class.java)
class JsonUtil @Inject constructor(
@ApplicationContext private val context: Context,
) {
fun readDatabaseBackupDataFromFile(
uri: Uri,
): DatabaseBackupData? {
val contentResolver = context.contentResolver
val stringBuilder = StringBuilder()
contentResolver.openInputStream(uri)?.use { inputStream ->
BufferedReader(InputStreamReader(inputStream)).use { bufferedReader ->
var line: String? = bufferedReader.readLine()
while (line != null) {
stringBuilder.append(line)
line = bufferedReader.readLine()
}
}
}
return databaseBackupDataJsonAdapter.fromJson(stringBuilder.toString())
}
fun writeDatabaseBackupDataToFile(
uri: Uri,
databaseBackupData: DatabaseBackupData,
) {
val jsonString = databaseBackupDataJsonAdapter.toJson(databaseBackupData)
writeJsonToFile(
uri = uri,
jsonString = jsonString,
)
}
private fun writeJsonToFile(
uri: Uri,
jsonString: String,
) {
val contentResolver = context.contentResolver
try {
contentResolver.openFileDescriptor(uri, "w")?.use {
FileOutputStream(it.fileDescriptor).use { fileOutputStream ->
fileOutputStream.write(jsonString.toByteArray())
}
}
} catch (fileNotFoundException: FileNotFoundException) {
fileNotFoundException.printStackTrace()
} catch (ioException: IOException) {
ioException.printStackTrace()
}
}
}