Hot Reload — technologia umożliwiająca aktualizację kodu działającej aplikacji mobilnej bez restartu i utraty bieżącego stanu. Deweloper zmienia kod źródłowy — po sekundzie zmiany są widoczne na ekranie urządzenia lub emulatora. To kluczowa cecha Flutter i React Native, radykalnie przyspieszająca iteracje programowania: czas cyklu edycja-podgląd skraca się z 5–10 sekund (przebudowa) do 300–500 milisekund. Według Flutter Documentation, 2025, hot reload wykonuje inkrementalną kompilację zmienionego kodu i wysyła aktualizację do Dart VM.
Najważniejsze
Hot Reload — mechanizm programowania, w którym kod źródłowy jest zmieniany i stosowany w już uruchomionej aplikacji bez jej zatrzymywania. Deweloper edytuje plik, zapisuje go i po 0.3–2 sekundach zaktualizowany interfejs pojawia się na ekranie. Stan aplikacji (liczniki, pozycja przewijania, wprowadzone dane) zostaje zachowany — deweloper nie traci kontekstu.
Koncepcja hot reload powstała we wczesnych narzędziach webowych (LiveReload, 2010) i została zaadaptowana do programowania mobilnego przez frameworki Flutter (2017) i React Native (2015). Obecnie hot reload to obowiązkowa funkcja nowoczesnych frameworków mobilnych, obok konfiguracji debug i profilowania. Bez hot reload programowanie UI uważa się za nieefektywne: każdy podgląd zmiany wymaga 10–30 sekund na przebudowę i uruchomienie.
Technicznie hot reload składa się z trzech kroków: wykrycie zmiany (file watcher), kompilacja zmienionego kodu (incremental compiler) i zastosowanie (hot patching). Każdy framework realizuje te kroki inaczej, ale wynik jest ten sam: minimalne opóźnienie między edycją a wyświetleniem.
Hot Reload we Flutter opiera się na architekturze Dart VM i kompilacji JIT. Gdy deweloper kliknie „Hot Reload” w IDE lub zapisze plik, Flutter wykonuje inkrementalną kompilację zmienionych bibliotek Dart do plików kernel (.dill). Dart VM ładuje te pliki i zastępuje implementacje zmienionych funkcji w działającej aplikacji.
// Flutter widget ze stanem zachowywanym przy hot reload
class CounterWidget extends StatefulWidget {
@override
State createState() => _CounterState();
}
class _CounterState extends State {
int _counter = 0;
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Licznik: $_counter'),
ElevatedButton(
onPressed: () => setState(() => _counter++),
child: Text('Zwiększ'),
),
],
);
}
}
W przykładzie StatefulWidget CounterWidget zachowuje pole _counter podczas hot reload. Dart VM odtwarza stan (State), wywołując reassemble(), ale nie zeruje _counter — wartość zostaje zachowana, jeśli widżet nie jest całkowicie odtwarzany. Flutter wywołuje reassemble() dla wszystkich obiektów State, a build() uruchamia się ponownie z aktualnym kodem i zachowanym stanem.
Kiedy hot reload nie działa: jeśli zmieniono statyczną zmienną inicjalizacyjną (static const), zmienną globalną, main(), deklarację klasy enum/mixin, kod w @override initState(). W takich przypadkach wymagany jest Hot Restart. Według danych Flutter Team (2025), hot reload udaje się w 85–90% przypadków; 10–15% zmian wymaga pełnego restartu.
Dart VM w trybie debug działa jako kompilator JIT: interpretuje kod Dart przez format kernel (analogiczny do bytecodu). Hot reload ładuje nowy plik kernel i zastępuje stare definicje funkcji. VM nie restartuje izolatów (isolates) — wszystkie operacje asynchroniczne (Future, Stream) działają dalej. W trybie release Dart kompiluje się AOT (dart2native), a hot reload jest niedostępny.
Fast Refresh (wcześnie Hot Reloading) w React Native używa Metro bundler — modułowego bundlera JavaScript, który śledzi zmiany plików. Gdy deweloper zapisuje plik, Metro kompiluje tylko zmieniony moduł (HMR — Hot Module Replacement) i wysyła aktualizację przez WebSocket do działającej aplikacji.
// Komponent React Native z zachowaniem stanu przy hot reload
import React, { useState } from 'react';
import { View, Text, Button } from 'react-native';
const Counter = () => {
const [count, setCount] = useState(0);
return (
<View>
<Text>Licznik: {count}Text>
<Button title="Zwiększ"
onPress={() => setCount(c => c + 1)} />
View>
);
};
Fast Refresh zachowuje stan React (useState, useReducer) podczas aktualizacji modułu. Metro HMR przesyła tylko diff zmienionego modułu — nie cały bundle. React Native używa React Fast Refresh, opracowanego przez zespół React (Dan Abramov, 2019): generuje nowy render dla komponentu, ale zachowuje stany hooków i props, jeśli sygnatura komponentu się nie zmieniła.
Fast Refresh nie działa przy zmianie: export komponentu, hooków (useEffect, useMemo), zależności modułowych i modułów natywnych (Java/Objective-C). Dla takich zmian wymagany jest Reload (pełne przeładowanie JS-bundla) lub Rebuild (przebudowa kodu natywnego). Czas fast refresh — 200–800 ms, pełne reload — 2–5 sekund.
Hot Reload i Hot Restart — dwa tryby aktualizacji kodu z różnymi scenariuszami użycia. Hot Reload nadaje się do zmian UI (style, układ, kolory, teksty), gdy struktura klas i typ stanu nie ulegają zmianie. Hot Restart jest konieczny przy zmianie sygnatur metod, dodawaniu nowych widżetów/komponentów w korzeniowym drzewie, zmianie initState i modułów natywnych.
| Cecha | Hot Reload | Hot Restart |
|---|---|---|
| Prędkość | 0.3–2 sekundy | 2–10 sekund |
| Zachowanie stanu | Tak (zmienne, state, stos nawigacji) | Nie (aplikacja uruchamia się od nowa) |
| Kompilacja | Inkrementalna (tylko zmiany) | Pełna rekompilacja Dart/JS |
| Kiedy używać | Poprawki UI, style, teksty, layout | Zmiana struktury, nowe moduły, kod natywny |
| Flutter | Hot Reload (R) | Hot Restart (Shift + R) |
| React Native | Fast Refresh | Reload (Cmd + R) |
Zalecana strategia: zaczynać od hot reload. Jeśli zmiany nie zostały zastosowane (IDE pokazuje „Reload needed”) — wykonać hot restart. We Flutter ikona przycisku się zmienia: błyskawica (⚡) dla hot reload, przekreślona błyskawica — jeśli wymagany jest restart. Efektywność programowania z hot reload jest wyższa o 40–60% w porównaniu z pełnymi przebudowami (dane JetBrains Developer Survey 2024).
Wstrzykiwanie kodu (code injection) — wspólny mechanizm hot reload używany przez wszystkie frameworki. Składa się z trzech faz. Pierwsza — wykrycie zmiany: file watcher (wbudowany w IDE) lub system plików (FSNotify) rejestruje zmianę pliku .dart, .js, .tsx. Druga — kompilacja: inkrementalny kompilator przekształca tylko zmieniony plik w reprezentację pośrednią (kernel .dill dla Dart, HMR-module dla JS). Trzecia — zastosowanie: nowy kod jest przesyłany na urządzenie i zastępuje stare definicje w pamięci działającej aplikacji.
Gorąca zamiana funkcji (hot patching) — technika, w której runtime zastępuje wskaźnik na funkcję (function pointer) w tablicy metod wirtualnych. Dart VM używa ClassTable — wewnętrznej struktury zawierającej wszystkie załadowane klasy. Przy hot reload VM znajduje klasę w ClassTable i zastępuje jej definicje funkcji nowymi z pliku kernel. Wszystkie istniejące instancje klasy automatycznie otrzymują nowe zachowanie.
// Flutter: callback reassemble do zarządzania stanem po hot reload
class MyWidget extends StatefulWidget {
@override
State createState() => _MyState();
}
mixin ReloadAware on State {
@override
void reassemble() {
super.reassemble();
// Resetowanie pamięci podręcznej lub danych po hot reload
clearCache();
}
}
W przykładzie mixin ReloadAware nadpisuje metodę reassemble(), którą Dart VM wywołuje na każdym obiekcie State po hot reload. Deweloper może zresetować pamięć podręczną, ponownie zainicjować zasoby lub wykonać migrację stanu. Bez tej metody stare dane mogą pozostać w pamięci podręcznej i spowodować niezgodność po aktualizacji widżetów.
Gorąca zamiana nie działa dla zmian wymagających realokacji pamięci na nowe pola, zmiany typu zmiennej w klasie, dodawania nowych pól w StatefulWidget, zmiany wartości enum lub parametrów generic. Te zmiany są niekompatybilne z istniejącymi obiektami w pamięci — Dart VM nie może „przetasować” pól w już zaalokowanych obiektach. W takich przypadkach wymagany jest gorący restart (hot restart) lub pełna przebudowa.
Natywne programowanie Android i iOS tradycyjnie nie ma pełnoprawnego hot reload. Android Studio z Android 11+ i AGP 4.2+ obsługuje Apply Changes: aktualizację kodu bez restartu aplikacji. Apply Changes działa przez Android Runtime (ART) — zastępuje implementacje metod w plikach dex na bieżąco. Jednak Apply Changes jest ograniczony: nie działa dla zmian zasobów (layout.xml, drawable), manifestu i bibliotek natywnych.
Apple wprowadziła Previews (SwiftUI Preview) w Xcode 15 (2023) — to nie jest hot reload w klasycznym rozumieniu. Previews kompilują sekcję podglądu oddzielnie od głównej aplikacji i pokazują wynik w canvasie Xcode. Przy zapisie pliku Preview aktualizuje się w 1–3 sekundy, ale stan aplikacji nie jest zachowywany. Dla projektów UIKit hot reload jest dostępny przez narzędzia zewnętrzne: InjectionIII (John Holdsworth) i SwiftHotReload.
Kotlin Multiplatform (KMP) od 2024 roku otrzymał eksperymentalne wsparcie hot reload od JetBrains. Mechanizm opiera się na Kotlin/Native runtime z zamianą funkcji w pliku obiektowym (.klib). JetBrains Compose Multiplayer używa własnej implementacji hot reload, podobnej do Flutter: inkrementalna kompilacja i zamiana klas w Kotlin/Native runtime. Szybkość — 1–3 sekundy, dostępne tylko dla zmian UI.
Apply Changes — mechanizm Android Studio, używający API ART runtime. Przy zapisie kodu Android Studio określa, które klasy uległy zmianie, i wysyła ich pliki dex na urządzenie przez adb. ART zastępuje implementacje metod w uruchomionej aplikacji bez zatrzymywania. Apply Changes działa w trzech trybach: Instant Run (szybka zamiana metody), Swap (zamiana klasy z odtworzeniem instancji) i Restart Activity (jeśli zmiany są niekompatybilne z bieżącym stanem).
Często zadawane pytania
Hot Reload aktualizuje kod bez restartu aplikacji i zachowuje stan. Live Reload przeładowuje całą aplikację lub stronę internetową przy zmianie plików. Live Reload jest prostszy w implementacji, ale wolniejszy i traci stan. Flutter i React Native używają hot reload, narzędzia webowe — live reload.
Hot Reload nie działa przy zmianach wymagających realokacji pamięci (nowe pola klasy), zmiany stałych statycznych (static const), zmiany nazw widżetów, zmiany enum lub parametrów generic. Te zmiany są niekompatybilne z istniejącymi obiektami w pamięci Dart VM lub JavaScript runtime.
Tak, hot reload działa zarówno na fizycznym urządzeniu, jak i na emulatorze. Flutter przesyła pliki kernel na urządzenie przez USB (adb forward) lub Wi-Fi. React Native używa WebSocket przez metro bundler. Opóźnienie na fizycznym urządzeniu jest zwykle o 10–30% wyższe niż na emulatorze.
Xcode Previews (od 2021) — odpowiednik hot reload dla SwiftUI, ale z ograniczeniami: podgląd jest kompilowany oddzielnie, nie obsługuje nawigacji po aplikacji i złożonych stanów. Apple nie udostępnia oficjalnego hot reload dla iOS. Narzędzia zewnętrzne: InjectionIII i SwiftHotReload używają Objective-C Runtime do wstrzykiwania kodu.
Jeśli po hot reload UI wyświetla się nieprawidłowo: wykonaj hot restart. Jeśli problem leży w danych — sprawdź callback reassemble() we Flutter lub useEffect cleanup w React Native. W przypadku stałych problemów użyj Flutter Clean lub Reset Metro Cache. Jeśli błąd powtarza się tylko po reload — to oznaka niekompatybilności zmian z istniejącym stanem.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również