Use streams to encode and decode the JSON backups

This significantly reduces memory usage.

(We buffer the OutputStream because kotlinx.serialization flushes its
internal buffers excessively[1]. We also buffer the InputStream for
consistency, even though kotlinx.serialization uses adequate
buffering; there is no performance impact because BufferedInputStream
cascades harmlessly[2].)

The functions encodeToStream() and decodeFromStream() are marked as
experimental, but this is a pretty fundamental use case so surely it
will continue to be supported in the future (maybe with minor
changes).

Fixes #6.

[1] https://github.com/Kotlin/kotlinx.serialization/blob/v1.6.3/formats/json/jvmMain/src/kotlinx/serialization/json/internal/JvmJsonStreams.kt#L46
[2] https://github.com/openjdk/jdk/blob/jdk-23%2B14/src/java.base/share/classes/java/io/BufferedInputStream.java#L339
This commit is contained in:
Tom Levy 2024-03-18 17:28:41 +00:00
parent 9534a1031a
commit a7edeae6f3
2 changed files with 8 additions and 10 deletions

View file

@ -7,8 +7,8 @@ import android.os.Build
import android.os.Bundle
import android.provider.DocumentsContract
import androidx.activity.result.contract.ActivityResultContracts
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.encodeToStream
import org.fossify.commons.activities.ManageBlockedNumbersActivity
import org.fossify.commons.dialogs.*
import org.fossify.commons.extensions.*
@ -118,6 +118,7 @@ class SettingsActivity : SimpleActivity() {
}
}
@OptIn(kotlinx.serialization.ExperimentalSerializationApi::class)
private fun exportMessages(uri: Uri) {
ensureBackgroundThread {
var success = false
@ -128,11 +129,8 @@ class SettingsActivity : SimpleActivity() {
return@getMessagesToExport
}
val json = Json { encodeDefaults = true }
val jsonString = json.encodeToString(messagesToExport)
val outputStream = contentResolver.openOutputStream(uri)!!
outputStream.use {
it.write(jsonString.toByteArray())
contentResolver.openOutputStream(uri)!!.buffered().use { outputStream ->
json.encodeToStream(messagesToExport, outputStream)
}
success = true
toast(org.fossify.commons.R.string.exporting_successful)

View file

@ -4,6 +4,7 @@ import android.net.Uri
import android.util.Xml
import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import org.fossify.commons.extensions.showErrorToast
import org.fossify.commons.extensions.toast
import org.fossify.commons.helpers.ensureBackgroundThread
@ -37,13 +38,12 @@ class MessagesImporter(private val activity: SimpleActivity) {
}
}
@OptIn(kotlinx.serialization.ExperimentalSerializationApi::class)
private fun importJson(uri: Uri) {
try {
val jsonString = activity.contentResolver.openInputStream(uri)!!.use { inputStream ->
inputStream.bufferedReader().readText()
val deserializedList = activity.contentResolver.openInputStream(uri)!!.buffered().use { inputStream ->
Json.decodeFromStream<List<MessagesBackup>>(inputStream)
}
val deserializedList = Json.decodeFromString<List<MessagesBackup>>(jsonString)
if (deserializedList.isEmpty()) {
activity.toast(org.fossify.commons.R.string.no_entries_for_importing)
return