2025年3月1日土曜日

KotlinのSharedFlowで、コレクターが存在するときだけ値を流す方法

KotlinのshareIn()はコールドFlowをホットFlowにすることができますが、

  • SharedFlowを使用し、コレクターが存在する場合にのみデータを流したい
  • 不要な処理を避け、パフォーマンスを最適化したい

といったケースでは、適切なフローの仕組みを選択する必要があります。

SharedFlowとは?

SharedFlowは、複数のコレクターが同じデータを受け取れるホットFlowの一種です。しかし、デフォルトではコレクターの有無にかかわらずデータが流れ続けるため、「コレクターがいないときは値を流さない」ように制御したほうがいい場合もあります。

SharedFlowの基本

2025年2月15日土曜日

Jetpack ComposeでのLaunchedEffectとwhileループの動作について

基本的な実装と動作

以下のようなコードでは、アプリ(Activity)がpause中でもwhileループは自動的には停止しません。

LaunchedEffect(key1 = Unit) {
     while (true) {
         indicatorAlpha.floatValue = 1f
         delay(1000L)
         indicatorAlpha.floatValue = 0f
         delay(1000L)
     }
}

なぜかというと、

  • LaunchedEffectはComposable関数のライフサイクルに連動するが、Activityのライフサイクルとは直接連動しない
  • whileループ内のdelayはsuspend関数だが、コルーチン自体は終了しない
  • ActivityがonPause()に入ってもコルーチンは実行を継続する

という理由があるためです。

ライフサイクルに応じた制御の実装

Activityのpause中にループを停止するには以下のような実装が必要です。

var isRunning by remember { mutableStateOf(true) }

LaunchedEffect(isRunning) {
    while (isRunning) {
        indicatorAlpha.floatValue = 1f
        delay(1000L)
        indicatorAlpha.floatValue = 0f
        delay(1000L)
    }
}

DisposableEffect(key1 = LocalLifecycleOwner.current) {
    val observer = LifecycleEventObserver { _, event ->
        if (event == Lifecycle.Event.ON_PAUSE) {
            isRunning = false
        } else if (event == Lifecycle.Event.ON_RESUME) {
            isRunning = true
        }
    }
    LocalLifecycleOwner.current.lifecycle.addObserver(observer)
    onDispose {
        LocalLifecycleOwner.current.lifecycle.removeObserver(observer)
    }
}

Decomposeを使用したMultiplatformアプリでの実装

Decomposeを使用している場合は、Decomposeのライフサイクルを使用することができます。

class YourComponent(
    componentContext: ComponentContext
) : ComponentContext by componentContext {

    private val _isRunning = MutableValue(true)
    val isRunning: Value<Boolean> = _isRunning

    init {
        lifecycle.doOnPause { _isRunning.value = false }
        lifecycle.doOnResume { _isRunning.value = true }
    }
}

まとめ

Jetpack ComposeでLaunchedEffectとwhileループを使用する際は、ActivityやDecomposeのライフサイクルを使って適切なタイミングでループを停止・再開することができます。 これにより、アプリがバックグラウンドに移行した際に不要な処理が実行され続けることを防いでバッテリー消費を抑えることができます。 回しっぱなしのループを作らないように心がけましょう。

2025年2月7日金曜日

MarkdownファイルをHTMLに変換してBloggerに掲載する方法

このブログではMarkdownファイルをローカルでHTMLに変換してBloggerにアップしています。その手順を説明します。

1. Pandocのインストール

私はMac miniをメインで使っているのでMacで作業する場合について説明します。 まずMarkdownファイルを変換するためのPandocをインストールします。 macOSではHomebrewを使用してPandocをインストールできます。

brew install pandoc

または公式サイトからダウンロードできます。

Pandoc 公式サイト

2. MarkdownをHTMLに変換する

最も基本的なコマンドでMarkdownをHTMLに変換できます。

pandoc -o output.html input.md

3. シンタックスハイライトを適用する

Bloggerでテーマの「HTMLを編集」を選択してテーマのHTMLを編集します。

<head> に以下を追加します。

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/kotlin.min.js"></script>
<script>hljs.highlightAll();</script>

4. 記事をコピー

作成したHTMLファイルをエディタで開き、ファイルの内容をBloggerのHTMLビューへコピペします。最初のh1タグの内容はタイトルなので本文ではなくタイトルの方にコピペします(タグは不要)。

これでMarkdownファイルからBloggerの記事を作成することができます。