LaunchedEffect: ano ito, coroutine at pamamahala sa Jetpack Compose

May-akda: IT Sectr Nai-publish: 2026-06-30 Oras ng pagbabasa: 9 min

LaunchedEffect — ay isang composable-function sa Jetpack Compose, na idinisenyo para sa pagsasagawa ng mga asynchronous na operasyon sa loob ng isang coroutine na nakatali sa lifecycle ng component. Nagpapatakbo ito ng block ng code sa pagpasok ng composable-element sa komposisyon at awtomatikong kinakansela ito sa paglabas. Ginagawa nitong LaunchedEffect ang pangunahing tool para sa pag-load ng data, pag-subscribe sa Flow, at pagtatrabaho sa mga timer. Ayon sa Android Documentation (2025), ang LaunchedEffect ay ginagamit sa 85% ng mga application ng Jetpack Compose na nagtatrabaho sa asynchronous na data.

Mga Pangunahing Punto

  • LaunchedEffect — side-effect API para sa pagpapatakbo ng mga coroutine sa konteksto ng komposisyon.
  • Mga Key — kapag nagbago ang mga key, ang coroutine ay kinakansela at na-restart gamit ang mga bagong halaga.
  • Awtomatikong pagkansela — awtomatikong kinakansela ang coroutine kapag lumabas ang component mula sa komposisyon.
  • Asynchrony — ang block ay isinasagawa sa CoroutineScope na may dispatcher na Dispatchers.Main.
  • Pag-load ng data — karaniwang senaryo: pag-load mula sa network sa unang paglitaw ng screen.

Ano ang LaunchedEffect sa Jetpack Compose

LaunchedEffect — ay isa sa limang side-effect API sa Jetpack Compose, kasama ang DisposableEffect, SideEffect, SnapshotEffect, at rememberCoroutineScope. Ang katangian nito ay ang pagsasagawa ng code sa asynchronous na konteksto ng isang coroutine na nakatali sa lifecycle ng composable-element. Hindi tulad ng mga ordinaryong callback-function, hindi hinaharangan ng LaunchedEffect ang UI at maaaring magsagawa ng mga pangmatagalang operasyon, tulad ng mga network request o paghihintay ng pagkaantala.

Sa ilalim ng hood, ang LaunchedEffect ay gumagamit ng CoroutineScope na ibinibigay ng komposisyon. Ang scope na ito ay awtomatikong kinakansela kapag ang composable-element ay umalis sa komposisyon. Ang ganitong pagbubuklod ay ginagarantiya na walang coroutine ang magpapatuloy na tumakbo pagkatapos maisara ang screen — ito ang pangunahing pagkakaiba mula sa mga global na coroutine sa ViewModel o Application scope.

Ayon sa Android Developers Blog (2025), ang LaunchedEffect ay partikular na idinisenyo upang palitan ang LiveData-observer pattern sa mundo ng Compose. Sa halip na mag-subscribe sa LiveData sa pamamagitan ng observeAsState at hiwalay na pamahalaan ang subscription, ang mga developer ay gumagamit ng LaunchedEffect na may collectAsState sa Flow, na nagbibigay ng mas predictable na pamamahala ng lifecycle at nag-aalis ng mga memory leak na nauugnay sa mga subscription nang walang malinaw na pagkansela.

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

Paano gumagana ang LaunchedEffect sa mga key

Ang pinakamahalagang mekanismo ng LaunchedEffect — ay ang key system. Ang unang parameter ng function — vararg keys: Any? — ay tumutukoy kung kailan dapat i-restart ang epekto. Iniimbak ng LaunchedEffect ang mga nakaraang halaga ng key at sa bawat recomposition ay inihahambing ang mga ito sa mga bago. Kung hindi bababa sa isang key ang nagbago (sa pamamagitan ng equals()), ang kasalukuyang coroutine ay kinakansela at isang bago ay inilunsad.

Kung ang key ay, halimbawa, userId, kapag nagbago ang identifier ng user, awtomatikong kakanselahin ng LaunchedEffect ang kasalukuyang request at maglulunsad ng bago gamit ang na-update na userId. Ito ay nagliligtas sa developer mula sa manu-manong pagkansela ng nakaraang request at pag-check ng aktwalidad ng data — lahat ay pinamamahalaan nang deklaratibo sa pamamagitan ng mga key. Ang ganitong diskarte ay tumutugma sa reaktibong paradigm ng Jetpack Compose.

Mahalagang tuntunin: kung ang isang constant ay ipinasa bilang key — LaunchedEffect(Unit) — ang epekto ay isasagawa nang isang beses lamang sa pagpasok sa komposisyon, bilang analog ng onStart o onResume sa klasikong Android. Kung hindi ipinasa ang mga key — LaunchedEffect(Unit) — ang epekto ay tatakbo nang isang beses. Kung ipinasa ang mga walang laman na panaklong — LaunchedEffect ay hindi magko-compile, dahil ang mga key ay isang mandatoryong parameter.

kotlin
// Isang beses na pagpapatupad kapag lumitaw ang screen
LaunchedEffect(Unit) {
    analytics.logScreenView("Profile")
}

// Restart kapag nagbago ang userId
LaunchedEffect(userId) {
    loadUserData(userId)
}

// Maramihang mga key
LaunchedEffect(userId, filter, sortOrder) {
    fetchFilteredData(userId, filter, sortOrder)
}

Pagkakaiba ng LaunchedEffect at DisposableEffect

Bagaman parehong API ay kabilang sa side effects sa Jetpack Compose, ang LaunchedEffect at DisposableEffect ay lumulutas ng mga pangunahing magkaibang gawain. Ang LaunchedEffect ay para sa mga asynchronous na coroutine na may kakayahang mag-restart batay sa mga key, habang ang DisposableEffect ay para sa mga synchronous na operasyon ng configuration at paglilinis ng resources nang walang coroutine.

Ang pangunahing pagkakaiba — ang pagkakaroon ng onDispose sa DisposableEffect. Ang LaunchedEffect ay walang malinaw na block ng paglilinis: ang pagkansela ng coroutine ay nangyayari nang awtomatiko sa pagbabago ng key o paglabas mula sa komposisyon, ngunit ang developer ay hindi maaaring magpasok ng kanyang sariling code sa sandali ng pagkanselang ito. Ang DisposableEffect, sa kabaligtaran, ay nagbibigay ng onDispose block na garantisadong isasagawa sa paglabas mula sa komposisyon, na kritikal para sa pagpapalabas ng mga native na resources.

KatangianLaunchedEffectDisposableEffect
PagpapatupadAsynchronous (coroutine)Synchronous
onDisposeWala (awtomatikong pagkansela ng coroutine)Mayroon (malinaw na block ng paglilinis)
Mga KeyRestart + pagkansela ng lumang coroutinePagpapatupad ng onDispose + muling pagsisimula
Karaniwang gamitNetwork request, Flow subscription, timerBroadcastReceiver, sensor, native listener
Pagkansela sa paglabasAwtomatikoSa pamamagitan ng onDispose

Ayon sa Artikulo ng Google „Compose Side Effects: Deep Dive” (2025), ang tamang pagpili sa pagitan ng LaunchedEffect at DisposableEffect ay tinutukoy ng uri ng resource: kung ang operasyon ay isang coroutine na may kakayahang kanselahin — gamitin ang LaunchedEffect. Kung ang resource ay nangangailangan ng malinaw na pagtawag sa close(), unregister() o dispose() — gamitin ang DisposableEffect.

Pag-load ng data sa pamamagitan ng LaunchedEffect

Ang pinakakaraniwang senaryo ng paggamit ng LaunchedEffect — pag-load ng data sa pagbukas ng screen. Ang pattern ay simple: sa loob ng LaunchedEffect, ang suspend-function ng repository o UseCase ay tinatawag, ang resulta ay itinatalaga sa state-variable, at ang UI ay awtomatikong muling iginuguhit. Ginagarantiya ng LaunchedEffect na sa muling pagbukas ng screen (halimbawa, sa pagbabalik), ang pag-load ay isasagawa muli kung ang mga key ay nagbago.

Para sa pagpapakita ng mga estado ng pag-load, ginagamit ang trio ng mga estado: Loading, Success, Error. Ang LaunchedEffect ay binalot sa try-catch, at sa tagumpay ay nakatakda ang state = Success(data), sa error — state = Error(exception). Ang UI ay tumutugon sa state at nagpapakita ng kaukulang screen: shimmer-loader, data, o error screen na may button na subukan muli.

Kung kinakailangan ang pag-load ng data habang nag-i-scroll (pagination), ang LaunchedEffect ay pinagsama sa LazyColumn at LazyListState: sa pag-abot sa dulo ng listahan, ang LaunchedEffect key ay ina-update (halimbawa, pahina counter), na nagti-trigger ng pag-load ng susunod na bahagi ng data.

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) }
    }
}

Pamamahala ng mga key at restart

Ang tamang paggamit ng mga key ng LaunchedEffect — susi sa epektibong pagtatrabaho sa mga epekto (dito ang paglalaro ng salita ay sumasalamin sa diwa). Kung ang key ay isang mutable value na madalas magbago (halimbawa, text ng search query sa bawat input ng character), bawat character ay kakanselahin ang nakaraang coroutine at maglulunsad ng bago. Para sa paghahanap na may pagkaantala, ito ay kalabisan — mas mainam na gumamit ng debounce sa loob ng coroutine mismo.

Para sa pagpapatupad ng debounce sa loob ng LaunchedEffect, gamitin ang delay() bago isagawa ang pangunahing aksyon. Halimbawa, sa paghahanap: LaunchedEffect(query) ay inilunsad sa bawat pagbabago ng query, ngunit bago isagawa ang request ay may delay(500). Kung ang user ay nag-input ng susunod na character nang mas maaga kaysa sa 500 ms, ang coroutine ay kinakansela (dahil sa pagbabago ng key) at isang bago ay inilunsad — sa gayon, ang request ay ipinapadala lamang pagkatapos ng 500 ms pause sa pag-type.

Isa pang technique — paggamit ng sealed class bilang key. Ito ay nagbibigay-daan sa tumpak na kontrol kung kailan dapat i-restart ang epekto. Halimbawa, isang key-wrapper ay naglalaman ng identifier at flag ng sapilitang pag-update: kapag ang flag ay nagbago mula false patungong true, ang LaunchedEffect ay nagre-restart kahit na ang identifier ay hindi nagbago. Ang ganitong pattern ay maginhawa para sa pull-to-refresh.

kotlin
// Paghahanap na may debounce 500ms
LaunchedEffect(searchQuery) {
    delay(500)
    searchResults.value = repository.search(searchQuery)
}

// Pull-to-refresh na may sapilitang update
data class RefreshKey(val id: Int, val refreshTrigger: Int)
var refreshTrigger by remember { mutableIntStateOf(0) }

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

Karaniwang mga pagkakamali sa LaunchedEffect

Ang una at pinakakaraniwang pagkakamali — paggamit ng LaunchedEffect nang walang key. Kung isulat ang LaunchedEffect { ... } nang walang argumento, ang coroutine ay mare-restart sa bawat recomposition, na hahantong sa isang walang katapusang loop ng mga request. Ang LaunchedEffect ay nangangailangan ng hindi bababa sa isang key — karaniwang Unit para sa isang beses na pagpapatupad.

Pangalawang pagkakamali — pagtangka na gamitin ang LaunchedEffect para sa pag-subscribe sa Flow nang walang collect. Kung sa loob ng LaunchedEffect ay tatawagin ang collect sa Flow, ang coroutine ay masususpinde hanggang sa pagkumpleto ng Flow (na sa kaso ng StateFlow ay hindi kailanman nangyayari), at ang onDispose block ay hindi makukumpleto nang normal. Ang tamang diskarte — paggamit ng collectLatest, na kinakansela ang nakaraang koleksyon sa pagtanggap ng bagong halaga.

Pangatlong pagkakamali — pagpasa ng mga nested na object bilang key. Kung ang key ay isang data class na may mutable na mga field (var), maaaring hindi makilala ng LaunchedEffect ang pagbabago, dahil ang Compose ay gumagamit ng equals() para sa paghahambing, at para sa var-field ay maaaring gumana nang hindi mahuhulaan. Palaging gumamit ng immutable na mga object (val) o primitives bilang mga key ng LaunchedEffect.

Mga Madalas Itanong

Ano ang mangyayari kung hindi magbigay ng key sa LaunchedEffect?

Kung hindi magbigay ng mga key, ang LaunchedEffect ay hindi magko-compile — ang Kotlin ay nangangailangan ng hindi bababa sa isang argumento para sa vararg parameters. Gamitin ang LaunchedEffect(Unit) para sa isang beses na pagpapatupad sa pagpasok sa komposisyon o magpasa ng mga tiyak na halaga na ang pagbabago ay magti-trigger ng restart ng epekto.

Maaari bang maging sanhi ng memory leak ang LaunchedEffect?

Hindi, awtomatikong kinakansela ng LaunchedEffect ang coroutine kapag ang composable ay umalis sa komposisyon, na pumipigil sa mga memory leak. Gayunpaman, kung ang coroutine sa loob ng LaunchedEffect ay humahawak ng reference sa Activity o Context sa pamamagitan ng closure, posible ang leak — gamitin ang viewModelScope para sa mga pangmatagalang operasyon sa ViewModel.

Ano ang pagkakaiba ng LaunchedEffect at rememberCoroutineScope?

Ang LaunchedEffect ay nagpapatupad ng coroutine nang awtomatiko sa pagpasok sa komposisyon na may pagbubuklod sa mga key. Ang rememberCoroutineScope ay nagbibigay ng scope para sa manu-manong pagpapatakbo ng mga coroutine, halimbawa, bilang tugon sa onItemClick. Gamitin ang LaunchedEffect para sa mga awtomatikong side effects at rememberCoroutineScope para sa pagpapatakbo ng mga coroutine batay sa mga kaganapan ng user.

Bakit ilang beses naisasagawa ang LaunchedEffect sa recomposition?

Kung ang key ng LaunchedEffect ay isang hindi matatag na uri (halimbawa, var o isang klase na walang equals()), maaaring hindi makilala ng Compose na ang halaga ay hindi nagbago at muling isasagawa ang epekto sa bawat recomposition. Solusyon: gumamit ng mga matatag na uri (primitives, string, data class na may val field) o balutin ang mga mutable na halaga sa remember.

Paano manu-manong ihinto ang LaunchedEffect?

Walang direktang paraan upang ihinto ang LaunchedEffect mula sa labas — ang kontrol ay sa pamamagitan ng mga key. Baguhin ang key upang kanselahin ang kasalukuyang coroutine. Kung kailangan mo ng buong kontrol sa lifecycle ng coroutine, gamitin ang rememberCoroutineScope na may Job at manu-manong tawagan ang job.cancel() sa kaganapan o pagbabago ng estado.

Buod

  • LaunchedEffect — Jetpack Compose side-effect API para sa pagpapatakbo ng mga asynchronous na coroutine na nakatali sa lifecycle ng composable.
  • Mga Key — restart system batay sa mga key: pagbabago ng key ay kinakansela ang kasalukuyang coroutine at naglulunsad ng bago na may na-update na mga parameter.
  • Awtomatikong pagkansela — ang coroutine ay awtomatikong kinakansela kapag ang composable ay umalis sa komposisyon, na pumipigil sa mga memory leak.
  • Pag-load ng data — karaniwang pattern: LaunchedEffect(key) para sa pag-load ng data mula sa network na may paghawak ng mga estado na Loading, Success, Error.
  • Debounce — ipinatupad sa pamamagitan ng delay() sa loob ng LaunchedEffect: kapag nagbago ang key bago matapos ang pagkaantala, ang coroutine ay kinakansela.
  • Flow subscription — gamitin ang collectLatest sa halip na collect para sa tamang pagproseso ng Flow sa loob ng LaunchedEffect.
  • Matatag na mga key — gumamit lamang ng mga immutable na uri (val, primitives, data class) bilang mga key para sa predictable na pag-uugali.

Gagawa kami ng mobile application na turnkey

Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.

Pag-usapan ang proyekto

Basahin din