Hot Reload — 動作中のモバイルアプリケーションのコードを、再起動せずに現在の状態を失うことなく更新できる技術です。開発者がソースコードを変更すると、1秒以内に変更がデバイスまたはエミュレーターの画面に表示されます。これはFlutterとReact Nativeの重要な機能であり、開発の反復を根本的に高速化します。編集から表示までのサイクル時間が5〜10秒(再ビルド)から300〜500ミリ秒に短縮されます。Flutter Documentation, 2025によると、hot reloadは変更されたコードのインクリメンタルコンパイルを実行し、更新をDart VMに送信します。
重要なポイント
Hot Reloadは、ソースコードを変更し、アプリケーションを停止することなく実行中のアプリケーションに適用する開発メカニズムです。開発者がファイルを編集して保存すると、0.3〜2秒以内に更新されたインターフェースが画面に表示されます。アプリケーションの状態(カウンター、スクロール位置、入力データ)は保持されるため、開発者はコンテキストを失いません。
hot reloadの概念は初期のWebツール(LiveReload、2010年)に起源を持ち、Flutter(2017年)とReact Native(2015年)によってモバイル開発に適応されました。現在、hot reloadはデバッグ設定やプロファイリングと並んで、最新のモバイルフレームワークの必須機能です。hot reloadなしでは、UI開発は非効率的と見なされます。変更を確認するたびに、再ビルドと起動に10〜30秒かかります。
技術的には、hot reloadは3つのステップで構成されます:変更検出(ファイルウォッチャー)、変更されたコードのコンパイル(インクリメンタルコンパイラ)、適用(ホットパッチング)。各フレームワークはこれらのステップを独自に実装しますが、結果は同じです:編集から表示までの最小限の遅延です。
FlutterでのHot Reloadは、Dart VMアーキテクチャとJITコンパイルに基づいています。開発者がIDEで“Hot Reload”を押すかファイルを保存すると、Flutterは変更されたDartライブラリをカーネルファイル(.dill)にインクリメンタルコンパイルします。Dart VMはこれらのファイルをロードし、実行中のアプリケーションで変更された関数の実装を置き換えます。
// Hot reload中に状態が保持されるFlutterのstatefulウィジェット
class CounterWidget extends StatefulWidget {
@override
State createState() => _CounterState();
}
class _CounterState extends State {
int _counter = 0;
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('カウンター: $_counter'),
ElevatedButton(
onPressed: () => setState(() => _counter++),
child: Text('増加'),
),
],
);
}
}
この例では、StatefulWidgetのCounterWidgetがhot reload中に_counterフィールドを保持します。Dart VMはreassemble()を呼び出して状態(State)を再作成しますが、_counterはリセットしません。ウィジェットが完全に再作成されない限り、値は保持されます。FlutterはすべてのStateオブジェクトに対してreassemble()を呼び出し、build()は更新されたコードと保持された状態で再実行されます。
hot reloadが機能しない場合:静的初期化変数(static const)、グローバル変数、main()、enum/mixinクラス宣言、@override initState()内のコードが変更された場合です。これらの場合、Hot Restartが必要です。Flutter Team(2025年)によると、hot reloadは85〜90%のケースで成功し、10〜15%の変更は完全な再起動が必要です。
Dart VMはデバッグモードでJITコンパイラとして動作します:カーネル形式(バイトコードに類似)を介してDartコードを解釈します。Hot reloadは新しいカーネルファイルをロードし、古い関数定義を置き換えます。VMはアイソレートを再起動せず、すべての非同期操作(Future、Stream)は継続して実行されます。リリースモードでは、DartはAOT(dart2native)でコンパイルされ、hot reloadは利用できません。
Fast Refresh(旧称Hot Reloading)はReact NativeでMetro bundlerを使用します — ファイル変更を監視するJavaScriptモジュールバンドラーです。開発者がファイルを保存すると、Metroは変更されたモジュールのみ(HMR — Hot Module Replacement)をコンパイルし、WebSocketを介して実行中のアプリケーションに更新を送信します。
// Hot reload中に状態を保持するReact Nativeコンポーネント
import React, { useState } from 'react';
import { View, Text, Button } from 'react-native';
const Counter = () => {
const [count, setCount] = useState(0);
return (
<View>
<Text>カウンター: {count}Text>
<Button title="増加"
onPress={() => setCount(c => c + 1)} />
View>
);
};
Fast Refreshはモジュール更新時にReactの状態(useState、useReducer)を保持します。Metro HMRは変更されたモジュールの差分のみを送信し、バンドル全体は送信しません。React NativeはReactチーム(Dan Abramov、2019年)が開発したReact Fast Refreshを使用します:コンポーネントの新しいレンダーを生成しますが、コンポーネントのシグネチャが変更されていない場合はフックの状態とプロパティを保持します。
Fast Refreshは以下の変更時に機能しません:コンポーネントのエクスポート、フック(useEffect、useMemo)、モジュールの依存関係、ネイティブモジュール(Java/Objective-C)の変更。このような変更にはReload(完全なJSバンドルの再読み込み)またはRebuild(ネイティブコードの再ビルド)が必要です。Fast refreshの時間は200〜800ミリ秒、完全なreloadは2〜5秒です。
Hot ReloadとHot Restartは、異なる使用シナリオを持つ2つのコード更新モードです。Hot Reloadは、クラス構造と状態のタイプが変更されない場合のUI変更(スタイル、レイアウト、色、テキスト)に適しています。Hot Restartは、メソッドシグネチャの変更、ルートツリーへの新しいウィジェット/コンポーネントの追加、initStateやネイティブモジュールの変更時に必要です。
| 特性 | Hot Reload | Hot Restart |
|---|---|---|
| 速度 | 0.3〜2秒 | 2〜10秒 |
| 状態の保持 | はい(変数、状態、ナビゲーションスタック) | いいえ(アプリケーションは新規起動) |
| コンパイル | インクリメンタル(変更のみ) | Dart/JSの完全再コンパイル |
| 使用時 | UI調整、スタイル、テキスト、レイアウト | 構造変更、新しいモジュール、ネイティブコード |
| Flutter | Hot Reload(R) | Hot Restart(Shift + R) |
| React Native | Fast Refresh | Reload(Cmd + R) |
推奨戦略:hot reloadから始めます。変更が適用されない場合(IDEに“Reload needed”と表示される)は、hot restartを実行します。Flutterでは、ボタンアイコンが変わります:hot reloadは雷(⚡)、restartが必要な場合は取り消し線の雷です。完全な再ビルドと比較して、hot reloadによる開発効率は40〜60%向上します(JetBrains Developer Survey 2024のデータ)。
コードインジェクション(code injection)は、すべてのフレームワークで使用されるhot reloadの一般的なメカニズムです。3つのフェーズで構成されます。第1 — 変更検出:ファイルウォッチャー(IDEに組み込み)またはファイルシステム(FSNotify)が.dart、.js、.tsxファイルの変更を検出します。第2 — コンパイル:インクリメンタルコンパイラが変更されたファイルのみを中間表現(Dartの場合はカーネル.dill、JSの場合はHMRモジュール)に変換します。第3 — 適用:新しいコードがデバイスに送信され、実行中のアプリケーションのメモリ内の古い定義を置き換えます。
ホットパッチング(hot patching)は、ランタイムが仮想メソッドテーブルの関数ポインタを置き換える技術です。Dart VMはClassTable — ロードされたすべてのクラスを含む内部構造を使用します。hot reload中、VMはClassTableでクラスを見つけ、カーネルファイルからの新しい関数定義でその関数定義を置き換えます。既存のクラスインスタンスはすべて自動的に新しい動作を取得します。
// Flutter: hot reload後の状態管理のためのreassembleコールバック
class MyWidget extends StatefulWidget {
@override
State createState() => _MyState();
}
mixin ReloadAware on State {
@override
void reassemble() {
super.reassemble();
// Hot reload後にキャッシュまたはデータをリセット
clearCache();
}
}
この例では、ReloadAwareミックスインがreassemble()メソッドをオーバーライドします。このメソッドは、hot reload後にDart VMが各Stateオブジェクトで呼び出します。開発者はキャッシュをリセットしたり、リソースを再初期化したり、状態の移行を実行したりできます。このメソッドがないと、古いデータがキャッシュに残り、ウィジェット更新後に不整合を引き起こす可能性があります。
ホットパッチングは、新しいフィールドのメモリ再割り当て、クラス内の変数タイプの変更、StatefulWidgetへの新しいフィールドの追加、enum値やジェネリックパラメータの変更が必要な変更には機能しません。これらの変更はメモリ内の既存オブジェクトと互換性がありません — Dart VMは既に割り当てられたオブジェクトのフィールドを“シャッフル”できません。このような場合、hot restartまたは完全な再ビルドが必要です。
ネイティブAndroidおよびiOS開発では、従来、完全なhot reloadはありません。Android StudioはAndroid 11+およびAGP 4.2+でApply Changesをサポートしています:アプリケーションを再起動せずにコードを更新します。Apply ChangesはAndroid Runtime(ART)を介して動作し、実行中にdexファイルのメソッド実装を置き換えます。ただし、Apply Changesには制限があります:リソース変更(layout.xml、drawable)、マニフェスト、ネイティブライブラリには機能しません。
AppleはXcode 15(2023年)でPreviews(SwiftUI Preview)を導入しました — これは古典的な意味でのhot reloadではありません。Previewsはメインアプリケーションとは別にプレビューセクションをコンパイルし、Xcodeのキャンバスに結果を表示します。ファイルを保存すると、プレビューは1〜3秒で更新されますが、アプリケーションの状態は保持されません。UIKitプロジェクトの場合、hot reloadはサードパーティツール(InjectionIII(John Holdsworth)、SwiftHotReload)を介して利用できます。
Kotlin Multiplatform(KMP)は2024年からJetBrainsによる実験的なhot reloadサポートを受けました。このメカニズムは、オブジェクトファイル(.klib)内の関数置き換えを伴うKotlin/Nativeランタイムに基づいています。JetBrains Compose MultiplayerはFlutterと同様の独自のhot reload実装を使用します:Kotlin/Nativeランタイムでのインクリメンタルコンパイルとクラス置き換え。速度は1〜3秒で、UI変更のみ利用可能です。
Apply Changesは、ARTランタイムAPIを使用するAndroid Studioのメカニズムです。コード保存時に、Android Studioはどのクラスが変更されたかを判断し、adbを介してデバイスにそのdexファイルを送信します。ARTは停止せずに実行中のアプリケーションのメソッド実装を置き換えます。Apply Changesは3つのモードで動作します:Instant Run(高速メソッド置き換え)、Swap(インスタンス再作成を伴うクラス置き換え)、Restart Activity(変更が現在の状態と互換性がない場合)。
よくある質問
Hot Reloadはアプリケーションを再起動せずにコードを更新し、状態を保持します。Live Reloadはファイル変更時にアプリケーション全体またはWebページを再読み込みします。Live Reloadは実装が簡単ですが、遅く、状態を失います。FlutterとReact Nativeはhot reloadを使用し、Webツールはlive reloadを使用します。
Hot Reloadは、メモリの再割り当てが必要な変更(新しいクラスフィールド)、静的定数(static const)の変更、ウィジェットの名前変更、enumやジェネリックパラメータの変更では機能しません。これらの変更は、Dart VMまたはJavaScriptランタイムのメモリ内の既存オブジェクトと互換性がありません。
はい、hot reloadは物理デバイスとエミュレーターの両方で動作します。FlutterはUSB(adb forward)またはWi-Fiを介してデバイスにカーネルファイルを送信します。React NativeはMetro bundlerを介してWebSocketを使用します。物理デバイスでの遅延は通常、エミュレーターより10〜30%高くなります。
Xcode Previews(2021年以降)はSwiftUI向けのhot reloadの類似機能ですが、制限があります:プレビューは個別にコンパイルされ、アプリケーションナビゲーションや複雑な状態をサポートしません。AppleはiOS向けの公式hot reloadを提供していません。サードパーティツール:InjectionIIIとSwiftHotReloadはコードインジェクションにObjective-C Runtimeを使用します。
hot reload後にUIが正しく表示されない場合:hot restartを実行します。データに問題がある場合:Flutterのreassemble()コールバックまたはReact NativeのuseEffect cleanupを確認します。永続的な問題には、Flutter CleanまたはReset Metro Cacheを使用します。バグがreload後にのみ再現する場合 — それは変更と既存の状態の非互換性の兆候です。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。