[Androidアプリ開発] Jetpack ComposeでrememberやmutableStateOfの違い

AndroidアプリをKotlin言語+Jetpack Composeで開発する際に、以下のようなコードが散見します。

@Composable
fun Foo() {
    val n1 = remember { 1 }
    val n2 = remember { mutableStateOf(2) }
    var n3 by remember { mutableStateOf(3) }
}

このn1、n2、n3、の違いを確認します。

前提

rememberは、Composable関数が再コンポーズされる間、指定した値を記憶します。

n1

値が一度セットされたら、変更されることがなく、その値を保持し続けたい場合に使います。ここでは、値として1を単に記憶させているだけです。 これによりn1は再コンポーズの間に値が変更されない定数のようなものになります。

より具体的な例を2つ示します。

@Composable
fun ItemList() {
    val itemCount = remember { 10 } // 固定のアイテム数を保持

    LazyColumn {
        items(itemCount) { index ->
            Text(text = "Item #$index")
        }
    }
}
@Composable
fun ColoredBox() {
    val fixedColor = remember { Color.Red } // 固定の色を保持
    Box(
        modifier = Modifier
            .size(100.dp)
            .background(fixedColor)
    )
}

単純に値を保持するだけであれば、定数として宣言しても問題ありません。

@Composable
fun Foo() {
    val itemCount = 10
    val fixedColor = Color.Red
}

一方、rememberを使うと、再コンポーズ時に再計算を避けられます。最初の一度だけ実行すればよい処理に適用すると、パフォーマンスが向上します。

@Composable
fun ExpensiveCalculationExample() {
    val expensiveResult = remember { performExpensiveCalculation() } // ※

    Text(text = "Result:$expensiveResult")
}

fun performExpensiveCalculation(): Int {
    // ここで重い計算を実行
    return 42
}

この※の部分を、「val expensiveResult = performExpensiveCalculation()」としてしまうと、再コンポーズのたびに実行されてしまう、すなわち時間がかかります。

ちなみに、itemCountやflexColorのような小さなデータは、ActivityやFragmentの定数として宣言すると良いでしょう。

class MyActivity : ComponentActivity() {

    companion object {
        const val ITEM_COUNT = 10
        val FIXED_COLOR = Color.Red
    }

    @Composable
    fun ItemList() {
        LazyColumn {
            items(ITEM_COUNT) { index ->
                Text(text = "Item #$index")
            }
        }
    }

    @Composable
    fun ColoredBox() {
    Box(
        modifier = Modifier
            .size(100.dp)
            .background(FIXED_COLOR)
        )
    }

    override
    fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ItemList()
        ColoredBox()
        }
    }
}

n2とn3の場合

byの有無で、アクセス方法(扱い方)が変わります。

val n2 = remember { mutableStateOf(2) }

Text(text ="値:${n2.value}")
Button(onClick = { n2.value++ }) {
    Text("インクリメントする")
}

byを用いずに宣言したn2は、値を参照・更新する時にn2.valueと書きます。明示的にStateオブジェクトを管理したい時は、valと合わせて使うと便利です。

var n3 by remember { mutableStateOf(3) }
Text(text = "値: $n3")
Button(onClick = { n3++ }) {
    Text("インクリメントする")
}

byを用いて宣言したn3は、デリゲートプロパティを使ってよりシンプルに値を参照・更新できます。この場合はvarを使いましょう。また、複数の場所でStateの値を直接参照したい時に便利です。

n2とn3の違いは以上です。

こうしておけば、効率的なメモリ使用: 特定の型に最適化されているため、パフォーマンスが向上します。それに、 型が明確になり、意図がよりはっきりします(コードの明確化)。

ちなみに、

  • mutableStateOf(任意の型)
  • mutableIntStateOf
  • mutableLongStateOf
  • mutableFloatStateOf

はあるけど、
mutableDoubleStateOfやmutableStringStateOfは存在しないみたいです。
mutableStateOfが既に十分な汎用性を持っており、特にStringやDoubleに対しても最適に機能するためらしいです。

以上です。

弊社ではAndroidアプリ開発の研修コースを各種取り揃えております。ぜひご受講ください。https://www.casareal.co.jp/ls/service/openseminar/search/mobileapp

--------------------------
開発支援・技術研修のご要望・ご相談はこちらから
--------------------------
【この技術ブログを読んだエンジニアの皆様へ】
カサレアルブログをお読みいただき、ありがとうございます!

私たちは、常に新しい技術に挑戦し、ユーザーのニーズに応えるサービスを提供しています。
もし、当社の技術への情熱や、会社・チーム・社員の雰囲気に共感いただけたなら、
ぜひ私たちと一緒に働きませんか?
現在、株式会社カサレアルでは事業拡大に伴い、新たな仲間となるエンジニアを積極的に募集しています。

少しでも興味をお持ちいただけましたら、まずは弊社のことを知っていただけると嬉しいです。
▼採用サイト
https://www.casareal.co.jp/recruit/career
▼社員インタビュー
https://hrmos.co/pages/casareal/jobs/0000016
▼エンジニアの仲間になる! エントリーはこちらから
https://hrmos.co/pages/casareal/jobs

皆様のエントリーを心よりお待ちしています!

Unicodeの互換等価をJavaで扱う方法

コメントを残す

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です

コメント ※

名前 ※

メール ※

サイト