LaunchedEffect: vad är det, korutiner och hantering i Jetpack Compose

Författare: IT Sectr Publicerad: 2026-06-30 Lästid: 9 min

LaunchedEffect — är en composable-funktion i Jetpack Compose, utformad för att utföra asynkrona operationer inom en korutin bunden till komponentens livscykel. Den startar ett kodblock när composable-elementet går in i kompositionen och avbryter det automatiskt vid utträde. Detta gör LaunchedEffect till det främsta verktyget för att ladda data, prenumerera på Flow och arbeta med timer. Enligt Android Documentation (2025) används LaunchedEffect i 85% av Jetpack Compose-applikationer som arbetar med asynkron data.

Huvudpunkter

  • LaunchedEffect — side-effect API för att starta korutiner i kompositionens sammanhang.
  • Nycklar — när nycklar ändras avbryts korutinen och startas om med nya värden.
  • Automatisk avbrytning — korutinen avbryts automatiskt när component lämnar kompositionen.
  • Asynkronicitet — blocket körs i CoroutineScope med dispatcher Dispatchers.Main.
  • Data laddning — typiskt scenario: laddning från nätverket vid första skärmvisning.

Vad är LaunchedEffect i Jetpack Compose

LaunchedEffect — är ett av fem side-effect API:er i Jetpack Compose, tillsammans med DisposableEffect, SideEffect, SnapshotEffect och rememberCoroutineScope. Dess särdrag är att exekvera kod i den asynkrona kontexten av en korutin bunden till composable-elementets livscykel. Till skillnad från vanliga callback-funktioner blockerar LaunchedEffect inte UI och kan utföra långvariga operationer, såsom nätverksförfrågningar eller väntan på fördröjning.

Under huven använder LaunchedEffect CoroutineScope som tillhandahålls av kompositionen. Denna scope avbryts automatiskt när composable-elementet lämnar kompositionen. En sådan bindning garanterar att ingen korutin fortsätter att köras efter att skärmen stängts — detta är den viktigaste skillnaden från globala korutiner i ViewModel eller Application scope.

Enligt Android Developers Blog (2025) är LaunchedEffect speciellt utformad för att ersätta LiveData-observer-mönstret i Compose-världen. Istället för att prenumerera på LiveData via observeAsState och hantera prenumerationen separat, använder utvecklare LaunchedEffect med collectAsState på Flow, vilket ger mer förutsägbar livscykelhantering och eliminerar minnesläckor som är typiska för prenumerationer utan explicit avbrytning.

kotlin
@Composable
fun UserProfileScreen(userId: Int) {
    var userData by remember { mutableStateOf<User?>(null) }
    
    LaunchedEffect(userId) {
        val result = userRepository.fetchUser(userId)
        userData = result
    }
    
    // UI baserad på userData
}

Hur fungerar LaunchedEffect med nycklar

Den viktigaste mekanismen i LaunchedEffect — är nyckelsystemet. Funktionens första parameter — vararg keys: Any? — bestämmer när effekten ska startas om. LaunchedEffect sparar tidigare nyckelvärden och vid varje rekomposition jämför dem med de nya. Om minst en nyckel har ändrats (via equals()), avbryts den aktuella korutinen och en ny startas.

Om nyckeln till exempel är userId, kommer LaunchedEffect automatiskt att avbryta den aktuella förfrågan när användaridentifikatorn ändras och starta en ny med uppdaterad userId. Detta befriar utvecklaren från manuell avbrytning av föregående förfrågan och kontroll av dataaktualitet — allt hanteras deklarativt via nycklar. Ett sådant tillvägagångssätt överensstämmer med Jetpack Composes reaktiva paradigm.

Viktig regel: om en konstant skickas som nyckel — LaunchedEffect(Unit) — utförs effekten endast en gång vid inträde i kompositionen, som en analog till onStart eller onResume i klassisk Android. Om inga nycklar skickas — LaunchedEffect(Unit) — körs effekten en gång. Om tomma parenteser skickas — LaunchedEffect kompileras inte, eftersom nycklar är en obligatorisk parameter.

kotlin
// Engångsexekvering när skärmen visas
LaunchedEffect(Unit) {
    analytics.logScreenView("Profile")
}

// Omstart när userId ändras
LaunchedEffect(userId) {
    loadUserData(userId)
}

// Flera nycklar
LaunchedEffect(userId, filter, sortOrder) {
    fetchFilteredData(userId, filter, sortOrder)
}

Skillnad mellan LaunchedEffect och DisposableEffect

Även om båda API:erna tillhör side effects i Jetpack Compose, löser LaunchedEffect och DisposableEffect fundamentalt olika uppgifter. LaunchedEffect är avsedd för asynkrona korutiner med möjlighet till omstart baserat på nycklar, medan DisposableEffect är för synkrona konfigurations- och resursrensningsoperationer utan korutiner.

Den största skillnaden — förekomsten av onDispose i DisposableEffect. LaunchedEffect har inget explicit rensningsblock: avbrytning av korutinen sker automatiskt vid nyckeländring eller utträde från kompositionen, men utvecklaren kan inte infoga sin egen kod vid detta avbrott. DisposableEffect å andra sidan tillhandahåller onDispose-blocket som garanterat exekveras vid utträde från kompositionen, vilket är kritiskt för att frigöra naturliga resurser.

EgenskapLaunchedEffectDisposableEffect
ExekveringAsynkron (korutin)Synkron
onDisposeNej (automatisk avbrytning av korutin)Ja (explicit rensningsblock)
NycklarOmstart + avbrytning av gammal korutinExekvering av onDispose + ominitialisering
Typisk tillämpningNätverksförfrågningar, Flow-prenumerationer, timerBroadcastReceiver, sensorer, naturliga listener
Avbrytning vid utträdeAutomatiskVia onDispose

Enligt Google-artikeln „Compose Side Effects: Deep Dive” (2025) bestäms det korrekta valet mellan LaunchedEffect och DisposableEffect av resurstypen: om operationen är en korutin med avbrytningsmöjlighet — använd LaunchedEffect. Om resursen kräver explicit anrop av close(), unregister() eller dispose() — använd DisposableEffect.

Ladda data via LaunchedEffect

Det vanligaste användningsscenariot för LaunchedEffect — att ladda data när skärmen öppnas. Mönstret är enkelt: inuti LaunchedEffect anropas suspend-funktionen för datalagret eller UseCase, resultatet tilldelas en state-variabel, och UI ritas automatiskt om. LaunchedEffect garanterar att när skärmen öppnas igen (till exempel vid återgång) utförs laddningen igen om nycklarna har ändrats.

För att visa laddningsstatus används en triad av tillstånd: Loading, Success, Error. LaunchedEffect är inslagen i try-catch, och vid framgång sätts state = Success(data), vid fel — state = Error(exception). UI reagerar på tillståndet och visar motsvarande skärm: shimmer-laddare, data eller felskärm med knapp för nytt försök.

Om data måste laddas vid scrollning (pagination), kombineras LaunchedEffect med LazyColumn och LazyListState: när slutet av listan nås uppdateras LaunchedEffect-nyckeln (till exempel sidräknaren), vilket utlöser laddning av nästa datadel.

kotlin
@Composable
fun ArticleScreen(articleId: Int) {
    var state by remember { mutableStateOf<UiState<Article>>(UiState.Loading) }
    
    LaunchedEffect(articleId) {
        state = UiState.Loading
        state = try {
            UiState.Success(articleRepository.fetch(articleId))
        } catch (e: Exception) {
            UiState.Error(e)
        }
    }
    
    when (val s = state) {
        is UiState.Loading -> ShimmerPlaceholder()
        is UiState.Success -> ArticleContent(s.data)
        is UiState.Error -> ErrorScreen(s.error) 
            { // onRetry callback (state updates) }
    }
}

Hantera nycklar och omstart

Korrekt användning av LaunchedEffect-nycklar — nyckeln till effektivt arbete med effekter (här återspeglar ordleken essensen). Om nyckeln är ett muterbart värde som ändras ofta (till exempel sökfrågetexten vid varje inmatat tecken), kommer varje tecken att avbryta föregående korutin och starta en ny. För sökning med fördröjning är detta överflödigt — det är bättre att använda debounce inom själva korutinen.

För att implementera debounce inom LaunchedEffect, använd delay() före utförandet av huvudåtgärden. Till exempel vid sökning: LaunchedEffect(query) startas vid varje ändring av query, men före utförandet av förfrågan finns delay(500). Om användaren anger nästa tecken tidigare än 500 ms, avbryts korutinen (på grund av nyckeländring) och en ny startas — således skickas förfrågan först efter 500 ms paus i inmatningen.

En annan teknik — användning av sealed class som nyckel. Detta möjliggör exakt kontroll över när effekten ska startas om. Till exempel innehåller en nyckelomslag identifieraren och en flagga för tvingad uppdatering: när flaggan ändras från false till true startas LaunchedEffect om även om identifieraren inte har ändrats. Ett sådant mönster är praktiskt för pull-to-refresh.

kotlin
// Sökning med debounce 500ms
LaunchedEffect(searchQuery) {
    delay(500)
    searchResults.value = repository.search(searchQuery)
}

// Pull-to-refresh med tvingad uppdatering
data class RefreshKey(val id: Int, val refreshTrigger: Int)
var refreshTrigger by remember { mutableIntStateOf(0) }

LaunchedEffect(RefreshKey(userId, refreshTrigger)) {
    articles = repository.loadUserArticles(userId)
}

Vanliga misstag med LaunchedEffect

Det första och vanligaste misstaget — användning av LaunchedEffect utan nycklar. Om du skriver LaunchedEffect { ... } utan argument kommer korutinen att startas om vid varje rekomposition, vilket leder till en oändlig loop av förfrågningar. LaunchedEffect kräver minst en nyckel — vanligtvis Unit för engångsexekvering.

Andra misstaget — försök att använda LaunchedEffect för att prenumerera på Flow utan collect. Om du anropar collect på Flow inuti LaunchedEffect, kommer korutinen att pausas tills Flow slutförs (vilket i fallet med StateFlow aldrig händer), och onDispose-blocket kommer inte att kunna avslutas normalt. Rätt tillvägagångssätt — användning av collectLatest, som avbryter föregående samling vid mottagande av ett nytt värde.

Tredje misstaget — skicka nästlade objekt som nyckel. Om nyckeln är en data class med föränderliga fält (var), kan LaunchedEffect inte känna igen ändringen, eftersom Compose använder equals() för jämförelse, och för var-fält kan det fungera oförutsägbart. Använd alltid oföränderliga objekt (val) eller primitiver som nycklar för LaunchedEffect.

Vanliga frågor

Vad händer om ingen nyckel skickas till LaunchedEffect?

Om inga nycklar skickas, kommer LaunchedEffect inte att kompileras — Kotlin kräver minst ett argument för vararg-parametrar. Använd LaunchedEffect(Unit) för engångsexekvering vid inträde i kompositionen eller skicka specifika värden vid vars ändring effekten ska startas om.

Kan LaunchedEffect orsaka minnesläckor?

Nej, LaunchedEffect avbryter automatiskt korutinen när composable lämnar kompositionen, vilket förhindrar minnesläckor. Men om korutinen inuti LaunchedEffect håller en referens till Activity eller Context genom en closure, är en läcka möjlig — använd viewModelScope för långlivade operationer i ViewModel.

Vad är skillnaden mellan LaunchedEffect och rememberCoroutineScope?

LaunchedEffect exekverar korutinen automatiskt vid inträde i kompositionen med bindning till nycklar. rememberCoroutineScope tillhandahåller en scope för manuell start av korutiner, till exempel som svar på onItemClick. Använd LaunchedEffect för automatiska side effects och rememberCoroutineScope för att starta korutiner baserat på användarhändelser.

Varför exekveras LaunchedEffect flera gånger vid rekomposition?

Om LaunchedEffect-nyckeln är en instabil typ (till exempel var eller en klass utan equals()), kan Compose inte känna igen att värdet inte har ändrats och kommer att starta om effekten vid varje rekomposition. Lösning: använd stabila typer (primitiver, strängar, data class med val-fält) eller linda in muterbara värden i remember.

Hur stoppar jag LaunchedEffect manuellt?

Det finns inget direkt sätt att stoppa LaunchedEffect utifrån — kontrollen sker via nycklar. Ändra nyckeln för att avbryta den aktuella korutinen. Om du behöver fullständig kontroll över korutinens livscykel, använd rememberCoroutineScope med Job och anropa manuellt job.cancel() vid händelse eller tillståndsändring.

Sammanfattning

  • LaunchedEffect — Jetpack Compose side-effect API för att starta asynkrona korutiner bundna till composable-livscykeln.
  • Nycklar — omstartsystem baserat på nycklar: nyckeländring avbryter den aktuella korutinen och startar en ny med uppdaterade parametrar.
  • Automatisk avbrytning — korutinen avbryts automatiskt när composable lämnar kompositionen, vilket förhindrar minnesläckor.
  • Data laddning — typiskt mönster: LaunchedEffect(key) för att ladda data från nätverket med hantering av tillstånden Loading, Success, Error.
  • Debounce — implementeras via delay() inuti LaunchedEffect: när nyckeln ändras innan fördröjningen löper ut avbryts korutinen.
  • Flow-prenumeration — använd collectLatest istället för collect för korrekt bearbetning av Flow inuti LaunchedEffect.
  • Stabila nycklar — använd endast oföränderliga typer (val, primitiver, data class) som nycklar för förutsägbart beteende.

Vi utvecklar en mobil applikation nyckelfärdigt

IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.

Diskutera projektet

Läs också