राज्य संरक्षण और स्थायी मेमोरी
संग्रह की मदद से व्यवस्थित रहें
अपनी प्राथमिकताओं के आधार पर, कॉन्टेंट को सेव करें और कैटगरी में बांटें.
Jetpack Compose
Jetpack Compose में, यूआई स्टेट को आम तौर पर
remember
और
rememberSaveable
.
हालांकि
rememberSaveable
सभी कॉन्फ़िगरेशन बदलावों में अपने-आप स्थिति बनाए रखने की सुविधा देता है,
ये क्षमताएं, शुरुआती डेटा टाइप और लागू करने वाले ऑब्जेक्ट तक ही सीमित होती हैं
Parcelable
या
Serializable
.
Brush
जैसे कस्टम ऑब्जेक्ट के लिए, साफ़ तौर पर सीरियलाइज़ेशन और डिसीरियलाइज़ेशन करने के तरीके ज़रूरी हैं. इन ऑब्जेक्ट में, नेस्ट किए गए जटिल स्ट्रक्चर और प्रॉपर्टी शामिल हो सकती हैं. यहां पर कस्टम स्टेट सेवर
उपयोगी हो जाता है.
इसके लिए Saver
:
Brush
ऑब्जेक्ट, जैसा
दिए गए उदाहरण में इस उदाहरण का इस्तेमाल करके, brushStateSaver
के साथ दिखाया गया है
Converters
क्लास के लिए इस्तेमाल किया जा सकता है, तो
कॉन्फ़िगरेशन में बदलाव होने के बाद भी, ब्रश के ज़रूरी एट्रिब्यूट का रखरखाव
होता है.
fun brushStateSaver(converters: Converters): Saver<MutableState<Brush>, String> = Saver(
save = { state ->
converters.brushToString(state.value)
},
restore = { jsonString ->
val brush = converters.stringToBrush(jsonString)
mutableStateOf(brush)
}
)
इसके बाद, कस्टम सेगमेंट बनाने के लिए,
Saver
से
उपयोगकर्ता के चुने गए ब्रश की स्थिति को इस तरह बनाए रखें:
val converters = Converters()
val currentBrush = rememberSaveable(saver = brushStateSaver(converters)) { mutableStateOf(defaultBrush) }
स्थायी जगह
दस्तावेज़ को सेव करने, लोड करने, और संभावित रीयल-टाइम जैसी सुविधाओं को चालू करने के लिए
सहयोग, स्टोर स्ट्रोक, और इससे जुड़े डेटा को सीरियल के तौर पर फ़ॉर्मैट में रखें.
Ink API, मैन्युअल तरीके से सीरियलाइज़ेशन और डीसीरियलाइज़ेशन की ज़रूरत है.
स्ट्रोक को सटीक तरीके से पहले जैसा करने के लिए, उसकेBrush
और [StrokeInputBatch
] को सेव करें.
सीरियलाइज़ेशन का बुनियादी तरीका
सीरियलाइज़ेशन ऑब्जेक्ट का ऐसा स्ट्रक्चर तय करें जो इंक लाइब्रेरी के ऑब्जेक्ट का डुप्लीकेट वर्शन बनाता हो.
Gson, Moshi, Protobuf वगैरह जैसे अपने पसंदीदा फ़्रेमवर्क का इस्तेमाल करके, सीरियलाइज़ किए गए डेटा को कोड में बदलें. साथ ही, ऑप्टिमाइज़ेशन के लिए कंप्रेसन का इस्तेमाल करें.
data class SerializedStroke(
val inputs: SerializedStrokeInputBatch,
val brush: SerializedBrush
)
data class SerializedBrush(
val size: Float,
val color: Long,
val epsilon: Float,
val stockBrush: SerializedStockBrush
)
enum class SerializedStockBrush {
MARKER_V1,
PRESSURE_PEN_V1,
HIGHLIGHTER_V1
}
data class SerializedStrokeInputBatch(
val toolType: SerializedToolType,
val strokeUnitLengthCm: Float,
val inputs: List<SerializedStrokeInput>
)
data class SerializedStrokeInput(
val x: Float,
val y: Float,
val timeMillis: Float,
val pressure: Float,
val tiltRadians: Float,
val orientationRadians: Float,
val strokeUnitLengthCm: Float
)
enum class SerializedToolType {
STYLUS,
TOUCH,
MOUSE,
UNKNOWN
}
class Converters {
private val gson: Gson = GsonBuilder().create()
companion object {
private val stockBrushToEnumValues =
mapOf(
StockBrushes.markerV1 to SerializedStockBrush.MARKER_V1,
StockBrushes.pressurePenV1 to SerializedStockBrush.PRESSURE_PEN_V1,
StockBrushes.highlighterV1 to SerializedStockBrush.HIGHLIGHTER_V1,
)
private val enumToStockBrush =
stockBrushToEnumValues.entries.associate { (key, value) -> value to key }
}
private fun serializeBrush(brush: Brush): SerializedBrush {
return SerializedBrush(
size = brush.size,
color = brush.colorLong,
epsilon = brush.epsilon,
stockBrush = stockBrushToEnumValues[brush.family] ?: SerializedStockBrush.MARKER_V1,
)
}
private fun serializeStrokeInputBatch(inputs: StrokeInputBatch): SerializedStrokeInputBatch {
val serializedInputs = mutableListOf<SerializedStrokeInput>()
val scratchInput = StrokeInput()
for (i in 0 until inputs.size) {
inputs.populate(i, scratchInput)
serializedInputs.add(
SerializedStrokeInput(
x = scratchInput.x,
y = scratchInput.y,
timeMillis = scratchInput.elapsedTimeMillis.toFloat(),
pressure = scratchInput.pressure,
tiltRadians = scratchInput.tiltRadians,
orientationRadians = scratchInput.orientationRadians,
strokeUnitLengthCm = scratchInput.strokeUnitLengthCm,
)
)
}
val toolType =
when (inputs.getToolType()) {
InputToolType.STYLUS -> SerializedToolType.STYLUS
InputToolType.TOUCH -> SerializedToolType.TOUCH
InputToolType.MOUSE -> SerializedToolType.MOUSE
else -> SerializedToolType.UNKNOWN
}
return SerializedStrokeInputBatch(
toolType = toolType,
strokeUnitLengthCm = inputs.getStrokeUnitLengthCm(),
inputs = serializedInputs,
)
}
private fun deserializeStroke(serializedStroke: SerializedStroke): Stroke? {
val inputs = deserializeStrokeInputBatch(serializedStroke.inputs) ?: return null
val brush = deserializeBrush(serializedStroke.brush) ?: return null
return Stroke(brush = brush, inputs = inputs)
}
private fun deserializeBrush(serializedBrush: SerializedBrush): Brush {
val stockBrushFamily = enumToStockBrush[serializedBrush.stockBrush] ?: StockBrushes.markerV1
return Brush.createWithColorLong(
family = stockBrushFamily,
colorLong = serializedBrush.color,
size = serializedBrush.size,
epsilon = serializedBrush.epsilon,
)
}
private fun deserializeStrokeInputBatch(
serializedBatch: SerializedStrokeInputBatch
): StrokeInputBatch {
val toolType =
when (serializedBatch.toolType) {
SerializedToolType.STYLUS -> InputToolType.STYLUS
SerializedToolType.TOUCH -> InputToolType.TOUCH
SerializedToolType.MOUSE -> InputToolType.MOUSE
else -> InputToolType.UNKNOWN
}
val batch = MutableStrokeInputBatch()
serializedBatch.inputs.forEach { input ->
batch.addOrThrow(
type = toolType,
x = input.x,
y = input.y,
elapsedTimeMillis = input.timeMillis.toLong(),
pressure = input.pressure,
tiltRadians = input.tiltRadians,
orientationRadians = input.orientationRadians,
)
}
return batch
}
fun serializeStrokeToEntity(stroke: Stroke): StrokeEntity {
val serializedBrush = serializeBrush(stroke.brush)
val serializedInputs = serializeStrokeInputBatch(stroke.inputs)
return StrokeEntity(
brushSize = serializedBrush.size,
brushColor = serializedBrush.color,
brushEpsilon = serializedBrush.epsilon,
stockBrush = serializedBrush.stockBrush,
strokeInputs = gson.toJson(serializedInputs),
)
}
fun deserializeEntityToStroke(entity: StrokeEntity): Stroke {
val serializedBrush =
SerializedBrush(
size = entity.brushSize,
color = entity.brushColor,
epsilon = entity.brushEpsilon,
stockBrush = entity.stockBrush,
)
val serializedInputs =
gson.fromJson(entity.strokeInputs, SerializedStrokeInputBatch::class.java)
val brush = deserializeBrush(serializedBrush)
val inputs = deserializeStrokeInputBatch(serializedInputs)
return Stroke(brush = brush, inputs = inputs)
}
fun brushToString(brush: Brush): String {
val serializedBrush = serializeBrush(brush)
return gson.toJson(serializedBrush)
}
fun stringToBrush(jsonString: String): Brush {
val serializedBrush = gson.fromJson(jsonString, SerializedBrush::class.java)
return deserializeBrush(serializedBrush)
}
}
इस पेज पर मौजूद कॉन्टेंट और कोड सैंपल कॉन्टेंट के लाइसेंस में बताए गए लाइसेंस के हिसाब से हैं. Java और OpenJDK, Oracle और/या इससे जुड़ी हुई कंपनियों के ट्रेडमार्क या रजिस्टर किए हुए ट्रेडमार्क हैं.
आखिरी बार 2025-07-27 (UTC) को अपडेट किया गया.
[[["समझने में आसान है","easyToUnderstand","thumb-up"],["मेरी समस्या हल हो गई","solvedMyProblem","thumb-up"],["अन्य","otherUp","thumb-up"]],[["वह जानकारी मौजूद नहीं है जो मुझे चाहिए","missingTheInformationINeed","thumb-down"],["बहुत मुश्किल है / बहुत सारे चरण हैं","tooComplicatedTooManySteps","thumb-down"],["पुराना","outOfDate","thumb-down"],["अनुवाद से जुड़ी समस्या","translationIssue","thumb-down"],["सैंपल / कोड से जुड़ी समस्या","samplesCodeIssue","thumb-down"],["अन्य","otherDown","thumb-down"]],["आखिरी बार 2025-07-27 (UTC) को अपडेट किया गया."],[],[],null,["# State preservation and persistent storage\n\nJetpack Compose\n---------------\n\nIn Jetpack Compose, UI state is typically managed using\n[`remember`](/reference/kotlin/androidx/compose/runtime/package-summary#remember(kotlin.Function0))\nand\n[`rememberSaveable`](/reference/kotlin/androidx/compose/runtime/saveable/package-summary#rememberSaveable(kotlin.Array,androidx.compose.runtime.saveable.Saver,kotlin.String,kotlin.Function0)).\nWhile\n[`rememberSaveable`](/reference/kotlin/androidx/compose/runtime/saveable/package-summary#rememberSaveable(kotlin.Array,androidx.compose.runtime.saveable.Saver,kotlin.String,kotlin.Function0))\noffers automatic state preservation across configuration changes, its built-in\ncapabilities are limited to primitive data types and objects that implement\n[`Parcelable`](/reference/kotlin/android/os/Parcelable) or\n[`Serializable`](/reference/java/io/Serializable).\n\nFor custom objects such as\n[`Brush`](/reference/kotlin/androidx/ink/brush/Brush), which may encompass\nintricate nested structures and properties, explicit serialization and\ndeserialization mechanisms are necessary. This is where a custom state saver\nbecomes useful. By defining a custom\n[`Saver`](/reference/kotlin/androidx/compose/runtime/saveable/Saver) for\nthe `Brush` object, as\ndemonstrated in the provided example with `brushStateSaver`using the example\n`Converters`class, you can guarantee the\npreservation of the brush's essential attributes even when configuration changes\noccur. \n\n fun brushStateSaver(converters: Converters): Saver\u003cMutableState\u003cBrush\u003e, String\u003e = Saver(\n save = { state -\u003e\n converters.brushToString(state.value)\n },\n restore = { jsonString -\u003e\n val brush = converters.stringToBrush(jsonString)\n mutableStateOf(brush)\n }\n )\n\nYou can then use the custom\n[`Saver`](/reference/kotlin/androidx/compose/runtime/saveable/Saver) to\npreserve a user's selected brush state like so: \n\n val converters = Converters()\n val currentBrush = rememberSaveable(saver = brushStateSaver(converters)) { mutableStateOf(defaultBrush) }\n\n### Persistent storage\n\nTo enable features such as document saving, loading, and potential real-time\ncollaboration, store strokes and associated data in a serialized format. For the\nInk API, manual serialization and deserialization are necessary.\n\nTo accurately restore a stroke, save its`Brush` and \\[`StrokeInputBatch`\\].\n\n- [**`Brush`**](/reference/kotlin/androidx/ink/brush/Brush): Includes numeric fields (size, epsilon), color, and [`BrushFamily`](/reference/kotlin/androidx/ink/brush/BrushFamily).\n- [**`StrokeInputBatch`**](/reference/kotlin/androidx/ink/strokes/StrokeInputBatch): Essentially a list of input points with numeric fields.\n\n#### Basic serialization\n\nDefine a serialization object structure that mirrors the Ink library objects.\n\nEncode the serialized data using your preferred framework like Gson, Moshi,\nProtobuf, and others, and use compression for optimization. \n\n data class SerializedStroke(\n val inputs: SerializedStrokeInputBatch,\n val brush: SerializedBrush\n )\n\n data class SerializedBrush(\n val size: Float,\n val color: Long,\n val epsilon: Float,\n val stockBrush: SerializedStockBrush\n )\n\n enum class SerializedStockBrush {\n MARKER_V1,\n PRESSURE_PEN_V1,\n HIGHLIGHTER_V1\n }\n\n data class SerializedStrokeInputBatch(\n val toolType: SerializedToolType,\n val strokeUnitLengthCm: Float,\n val inputs: List\u003cSerializedStrokeInput\u003e\n )\n\n data class SerializedStrokeInput(\n val x: Float,\n val y: Float,\n val timeMillis: Float,\n val pressure: Float,\n val tiltRadians: Float,\n val orientationRadians: Float,\n val strokeUnitLengthCm: Float\n )\n\n enum class SerializedToolType {\n STYLUS,\n TOUCH,\n MOUSE,\n UNKNOWN\n }\n\n class Converters {\n\n private val gson: Gson = GsonBuilder().create()\n\n companion object {\n private val stockBrushToEnumValues =\n mapOf(\n StockBrushes.markerV1 to SerializedStockBrush.MARKER_V1,\n StockBrushes.pressurePenV1 to SerializedStockBrush.PRESSURE_PEN_V1,\n StockBrushes.highlighterV1 to SerializedStockBrush.HIGHLIGHTER_V1,\n )\n\n private val enumToStockBrush =\n stockBrushToEnumValues.entries.associate { (key, value) -\u003e value to key }\n }\n\n private fun serializeBrush(brush: Brush): SerializedBrush {\n return SerializedBrush(\n size = brush.size,\n color = brush.colorLong,\n epsilon = brush.epsilon,\n stockBrush = stockBrushToEnumValues[brush.family] ?: SerializedStockBrush.MARKER_V1,\n )\n }\n\n private fun serializeStrokeInputBatch(inputs: StrokeInputBatch): SerializedStrokeInputBatch {\n val serializedInputs = mutableListOf\u003cSerializedStrokeInput\u003e()\n val scratchInput = StrokeInput()\n\n for (i in 0 until inputs.size) {\n inputs.populate(i, scratchInput)\n serializedInputs.add(\n SerializedStrokeInput(\n x = scratchInput.x,\n y = scratchInput.y,\n timeMillis = scratchInput.elapsedTimeMillis.toFloat(),\n pressure = scratchInput.pressure,\n tiltRadians = scratchInput.tiltRadians,\n orientationRadians = scratchInput.orientationRadians,\n strokeUnitLengthCm = scratchInput.strokeUnitLengthCm,\n )\n )\n }\n\n val toolType =\n when (inputs.getToolType()) {\n InputToolType.STYLUS -\u003e SerializedToolType.STYLUS\n InputToolType.TOUCH -\u003e SerializedToolType.TOUCH\n InputToolType.MOUSE -\u003e SerializedToolType.MOUSE\n else -\u003e SerializedToolType.UNKNOWN\n }\n\n return SerializedStrokeInputBatch(\n toolType = toolType,\n strokeUnitLengthCm = inputs.getStrokeUnitLengthCm(),\n inputs = serializedInputs,\n )\n }\n\n private fun deserializeStroke(serializedStroke: SerializedStroke): Stroke? {\n val inputs = deserializeStrokeInputBatch(serializedStroke.inputs) ?: return null\n val brush = deserializeBrush(serializedStroke.brush) ?: return null\n return Stroke(brush = brush, inputs = inputs)\n }\n\n private fun deserializeBrush(serializedBrush: SerializedBrush): Brush {\n val stockBrushFamily = enumToStockBrush[serializedBrush.stockBrush] ?: StockBrushes.markerV1\n\n return Brush.createWithColorLong(\n family = stockBrushFamily,\n colorLong = serializedBrush.color,\n size = serializedBrush.size,\n epsilon = serializedBrush.epsilon,\n )\n }\n\n private fun deserializeStrokeInputBatch(\n serializedBatch: SerializedStrokeInputBatch\n ): StrokeInputBatch {\n val toolType =\n when (serializedBatch.toolType) {\n SerializedToolType.STYLUS -\u003e InputToolType.STYLUS\n SerializedToolType.TOUCH -\u003e InputToolType.TOUCH\n SerializedToolType.MOUSE -\u003e InputToolType.MOUSE\n else -\u003e InputToolType.UNKNOWN\n }\n\n val batch = MutableStrokeInputBatch()\n\n serializedBatch.inputs.forEach { input -\u003e\n batch.addOrThrow(\n type = toolType,\n x = input.x,\n y = input.y,\n elapsedTimeMillis = input.timeMillis.toLong(),\n pressure = input.pressure,\n tiltRadians = input.tiltRadians,\n orientationRadians = input.orientationRadians,\n )\n }\n\n return batch\n }\n\n fun serializeStrokeToEntity(stroke: Stroke): StrokeEntity {\n val serializedBrush = serializeBrush(stroke.brush)\n val serializedInputs = serializeStrokeInputBatch(stroke.inputs)\n return StrokeEntity(\n brushSize = serializedBrush.size,\n brushColor = serializedBrush.color,\n brushEpsilon = serializedBrush.epsilon,\n stockBrush = serializedBrush.stockBrush,\n strokeInputs = gson.toJson(serializedInputs),\n )\n }\n\n fun deserializeEntityToStroke(entity: StrokeEntity): Stroke {\n val serializedBrush =\n SerializedBrush(\n size = entity.brushSize,\n color = entity.brushColor,\n epsilon = entity.brushEpsilon,\n stockBrush = entity.stockBrush,\n )\n\n val serializedInputs =\n gson.fromJson(entity.strokeInputs, SerializedStrokeInputBatch::class.java)\n\n val brush = deserializeBrush(serializedBrush)\n val inputs = deserializeStrokeInputBatch(serializedInputs)\n\n return Stroke(brush = brush, inputs = inputs)\n }\n\n fun brushToString(brush: Brush): String {\n val serializedBrush = serializeBrush(brush)\n return gson.toJson(serializedBrush)\n }\n\n fun stringToBrush(jsonString: String): Brush {\n val serializedBrush = gson.fromJson(jsonString, SerializedBrush::class.java)\n return deserializeBrush(serializedBrush)\n }\n\n }"]]