Lazy load bottom tabs

hotwired/hotwire-native-android

Bottom tab navigation now supports an opt‑in lazy loading mode that defers each tab’s initial screen until the tab is first selected.

背景

デフォルトでは、ボトムタブごとに NavigatorHost が起動時に即座に開始画面へ遷移し、すべてのタブが同時に WebView を生成します。タブが多数あるアプリでは、起動直後に多数の WebView が読み込まれ、リソース使用が集中します。PR の説明は、タブが実際にユーザーに選択されるまで開始地点のロードを遅延させることで、この集中を回避したいという要件に基づいています。実装は、既存の eager loading 挙動を変えずにオプトイン方式で提供されます。

技術的な変更

HotwireBottomNavigationController に新しいコンストラクタパラメータ lazyLoadTabs: Boolean = false が追加され、true を指定すると遅延ロードが有効になります。

class HotwireBottomNavigationController(
    val activity: HotwireActivity,
    val view: BottomNavigationView,
    val initialVisibility: Visibility = Visibility.DEFAULT,
    val clearNavigationOnTabReselection: Boolean = true,
    val animateVisibilityChanges: Boolean = true,
    val lazyLoadTabs: Boolean = false
) : NavController.OnDestinationChangedListener {
    // ...
}

デモアプリの MainActivity ではこのパラメータを true に設定してコントローラを生成しています。

bottomNavigationController = HotwireBottomNavigationController(
    activity = this,
    view = bottomNavigationView,
    lazyLoadTabs = true
)

HotwireActivityDelegatelazyNavigatorHostIds セットと setLazyNavigatorHosts メソッドが導入され、遅延対象のホスト ID を管理できるようになりました。

private val lazyNavigatorHostIds = mutableSetOf<Int>()

internal fun setLazyNavigatorHosts(navigatorHostIds: Collection<Int>) {
    lazyNavigatorHostIds.clear()
    lazyNavigatorHostIds.addAll(navigatorHostIds)
}

registerNavigatorHost の実装が変更され、遅延対象でかつ現在選択中でないホストは initControllerGraphIfNeeded の呼び出しがスキップされ、タブが初めて選択されたときに初期化されます。

if (host.id !in lazyNavigatorHostIds || currentNavigatorHostId == host.id) {
    host.initControllerGraphIfNeeded()
}

NavigatorHostisGraphInitialized フラグと initControllerGraphIfNeededresetControllerGraph が追加され、グラフの初期化とリセットを安全に遅延実行できるようになりました。

internal var isGraphInitialized = false
    private set

internal fun initControllerGraphIfNeeded() {
    if (!isGraphInitialized) {
        initControllerGraph()
    }
}

internal fun resetControllerGraph() {
    initControllerGraph()
}

さらに、Navigator.reset が内部で呼び出すメソッドを host.initControllerGraph() から host.resetControllerGraph() に変更し、リセット時に同様の遅延ロジックを利用できるよう統一しました。

clearAll {
    session.reset()
    host.resetControllerGraph()
    // ...
}

設計判断

遅延ロードは opt‑in として実装され、デフォルトは lazyLoadTabs = false のままで既存アプリへの影響を排除しています。開発者はコンストラクタでフラグを true に設定するだけで機能を有効化でき、コードベースの他の部分は変更不要です。

遅延対象はボトムタブに限定され、入れ子やスタンドアロンの navigator は従来通り eager に初期化されます。このスコープ限定は、遅延が意図しない領域に波及するリスクを抑える設計上の選択です。

実装は NavigatorHost の初期化タイミングを遅延させるだけで、既存ロジックやデータ構造への侵入は最小限に留められています。これによりコードベースの一貫性と保守性が維持されつつ、新機能が提供されます。

まとめ

この PR はボトムタブの初期ロードをタブ選択時に遅延させるオプトイン機構を追加し、既存の eager loading 動作を保持したまま柔軟な制御を可能にします。デフォルトは変更なしで動作し、必要に応じて lazyLoadTabs を有効化できる安全な拡張です。

記事メタデータ

Generated by:
gpt-oss-120b for DiffDaily
LLM Trace:
3e26fd72

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
リトライ後承認
Review Count:
2回 (改善を経て承認)
Reviewed by:
gpt-oss-120b for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文、背景、技術的な変更、設計判断(任意)、まとめの5つの要素が揃っており、総論→各論→結論の構成が明確です。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

コードブロックはファイル名付きハイライトが正しい形式で記述され、PRリンクは [#202](URL) の形で正しくリンク化されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

対象はAndroid/Kotlinに精通したエンジニアであり、専門用語や実装詳細が適切に記述されています。

パラグラフ・ライティング ⚠ WARNING

トピックセンテンス・1段落1トピック・段落長

各セクションの冒頭は要旨が述べられているものの、セクションごとの結論パラグラフが欠如しており、総論→各論→結論の完整な流れが一部不足しています。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内のコードブロックはDiffに記載された変更と完全に一致しており、ファイル名やコード内容も正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

用語はPRで使用されているものと一致し、誤用や不適切な表現は見られません。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

技術的な説明はPRの内容と合致し、因果関係や影響範囲が論理的に記述されています。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事の全主張はPRのタイトル・説明・Diffで裏付けられており、捏造や根拠のない推測はありません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号 #202 など数値・固有名詞は正確に記載されています。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事タイトルはPRタイトルと完全に一致しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

PRに記載されていないバージョン情報やリリース日程などの外部知識は含まれていません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

時間表現の歪曲はなく、PRの記述と一致しています。