Jetpack Compose の remember と remeberSaveable の動作を比べる
remember とは
- remember を利用するとコンポーザブル関数で特定の値を保持できる。
- 保持した値はコンポーザブル関数が再コンポーズされたときにのみ取得できる。
- もし Activity が破棄された場合には remember で保持した値は破棄される
rememberの動作を確認する
PLUS ボタンと MINUS ボタンを押した回数を rememberで値を保持するようにしてみた。
MainState: Counter 状態を保持するクラス
private val mainState = MainState()
class MainState {
private val _count: MutableStateFlow<Int> = MutableStateFlow(0)
val count: StateFlow<Int> = _count
fun plus() {
_count.value = _count.value + 1
}
fun minus() {
_count.value = _count.value - 1
}
}
MainActivity: Counter コンポーザブルを配置するクラス
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val count by mainState.count.collectAsState()
SampleTheme {
Counter(count = count, onPlus = { mainState.plus() }, onMinus = { mainState.minus() })
}
}
}
}
Counter: remember で値を保持するコンポーザブル関数
@Composable
private fun Counter(count: Int, onPlus: () -> Unit, onMinus: () -> Unit) {
var plusCount by remember { mutableStateOf(0) }
var minusCount by remember { mutableStateOf(0) }
Box(modifier = Modifier.fillMaxSize()) {
Column(
modifier = Modifier
.wrapContentHeight()
.fillMaxWidth()
.align(Alignment.Center)
) {
Text(text = "Counter", fontSize = 20.sp)
Text(text = "Count $count PlusCount $plusCount MinusCount $minusCount")
Button(
onClick = {
plusCount++
onPlus.invoke()
},
modifier = Modifier.fillMaxWidth()
) {
Text(text = "Plus")
}
Button(
onClick = {
minusCount++
onMinus.invoke()
},
modifier = Modifier.fillMaxWidth()
) {
Text(text = "Minus")
}
}
}
}
再コンポーズされても値が保持される
PLUS ボタンや MINUS ボタンを押すと MainState に保持されているカウント値が更新され Counter コンポーザブルが再コンポーズされます。PUS ボタンや MINUS ボタンを押した回数は remember で値を保持しているので再コンポーズされても値が保持されリセットされません。
Activity が破棄されると値は破棄される
開発者向けオプションで「アクティビティを保持しない」オプションを ON にして画面を再表示してみる。すると remember で保持した値は Activity を破棄されるので PLUS ボタンや MINUS ボタンのカウントは 0 になる。
rememberSaveable とは
- 基本的な動作は remember 同じ動作をする。
- もし Activity が破棄された場合にでも rememberSaveableで保持した値は破棄されない
rememberSaveable の動作を確認する
remember の動作確認で利用したコードを変更して PLUS ボタンと MINUS ボタンを押した回数を rememberSaveable で値を保持するようにしてみた。
Counter: rememberSaveableで値を保持するコンポーザブル関数
@Composable
private fun Counter(count: Int, onPlus: () -> Unit, onMinus: () -> Unit) {
var plusCount by rememberSaveable { mutableStateOf(0) }
var minusCount by rememberSaveable { mutableStateOf(0) }
Box(modifier = Modifier.fillMaxSize()) {
Column(
modifier = Modifier
.wrapContentHeight()
.fillMaxWidth()
.align(Alignment.Center)
) {
Text(text = "Counter", fontSize = 20.sp)
Text(text = "Count $count PlusCount $plusCount MinusCount $minusCount")
Button(
onClick = {
plusCount++
onPlus.invoke()
},
modifier = Modifier.fillMaxWidth()
) {
Text(text = "Plus")
}
Button(
onClick = {
minusCount++
onMinus.invoke()
},
modifier = Modifier.fillMaxWidth()
) {
Text(text = "Minus")
}
}
}
}
再コンポーズされても値が保持される
remember と同じで rememberSaveable でも再コンポーズされても値が保持される。
Activity が破棄されても値が保持される
remember の動作確認と同じ手順で開発者向けオプションで「アクティビティを保持しない」オプションを ON にして画面を再表示してみる。すると remember では破棄されていた値が rememberSaveable では保持されている。
remember や rememberSaveable でも非表示なったら値は保持できない
公式ドキュメントでも記載があるが remember や rememberSaveable では再コンポーズされた場合にのみ値を保持するらしい。以下のように特定のカウントに到達したら非表示にするコードを用意して再コンポーズされないケースの動作を確かめてみる。
MainActivity: 10を超えたら他のコンポーザブル関数を呼ぶ
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val count by mainState.count.collectAsState()
SampleTheme {
if (10 < count) {
CounterOver10(count, { mainState.plus() }, { mainState.minus() })
} else {
CounterLessThan10(count, { mainState.plus() }, { mainState.minus() })
}
}
}
}
}
CounterOver10: 10を超えた時に呼び出されるコンポーザブル関数
@Composable
private fun CounterOver10(count: Int, onPlus: () -> Unit, onMinus: () -> Unit) {
var plusCount by remember { mutableStateOf(0) }
var minusCount by remember { mutableStateOf(0) }
Box(modifier = Modifier.fillMaxSize()) {
Column(
modifier = Modifier
.wrapContentHeight()
.fillMaxWidth()
.align(Alignment.Center)
) {
Text(text = "CounterOver10", fontSize = 20.sp)
Text(text = "Count $count PlusCount $plusCount MinusCount $minusCount")
Button(
onClick = {
plusCount++
onPlus.invoke()
},
modifier = Modifier.fillMaxWidth()
) {
Text(text = "Plus")
}
Button(
onClick = {
minusCount++
onMinus.invoke()
},
modifier = Modifier.fillMaxWidth()
) {
Text(text = "Minus")
}
}
}
}
CounterLessThan10: 10時未満の時に呼び出されるコンポーザブル関数
@Composable
private fun CounterLessThan10(count: Int, onPlus: () -> Unit, onMinus: () -> Unit) {
var plusCount by remember { mutableStateOf(0) }
var minusCount by remember { mutableStateOf(0) }
Box(modifier = Modifier.fillMaxSize()) {
Column(
modifier = Modifier
.wrapContentHeight()
.fillMaxWidth()
.align(Alignment.Center)
) {
Text(text = "CounterLessThan10", fontSize = 20.sp)
Text(text = "Count $count PlusCount $plusCount MinusCount $minusCount")
Button(
onClick = {
plusCount++
onPlus.invoke()
},
modifier = Modifier.fillMaxWidth()
) {
Text(text = "Plus")
}
Button(
onClick = {
minusCount++
onMinus.invoke()
},
modifier = Modifier.fillMaxWidth()
) {
Text(text = "Minus")
}
}
}
}
やはり一度非表示になると値は保持できない
カウント値が10を超えると呼び出されるコンポーザブル関数が切り替わる。そして再度カウント値を10未満にしたらもとのコンポーザブル関数が呼び出されるが remember で保持した値は破棄される。
Discussion