แปลง Cold Flow เป็น Hot ด้วย shareIn และ stateIn
แปลง Flow แบบเย็นเป็นสตรีมแบบร้อนด้วยตัวดำเนินการแชร์
แปลง Cold Flow เป็น Hot ด้วย shareIn และ stateIn เป็นบทเรียน Kotlin Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Kotlin Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Kotlin Academy มีบทเรียนทั้งหมด 4 บทเรียน
โฟลว์เย็นกับโฟลว์ร้อน
โฟลว์เย็นจะเริ่มทำงานใหม่สำหรับตัวเก็บข้อมูลแต่ละตัว ส่วนโฟลว์ร้อนจะแชร์การสมัครรับข้อมูลจากต้นทางเพียงครั้งเดียว การแปลงโฟลว์เย็นเป็นโฟลว์ร้อนช่วยหลีกเลี่ยงงานซ้ำเมื่อมีตัวเก็บข้อมูลหลายตัวสมัครรับข้อมูล
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun expensiveFlow() = flow {
println("Starting upstream work") // runs once if shared
repeat(3) { delay(100); emit(it) }
}
fun main() = runBlocking {
val cold = expensiveFlow()
// Two collectors = two executions:
launch { cold.collect { } }
launch { cold.collect { } }
delay(500)
coroutineContext.cancelChildren()
}พื้นฐานของ shareIn
flow.shareIn(scope, started, replay) แปลง Flow เย็นให้เป็น SharedFlow โดยแชร์การสมัครรับข้อมูลจากต้นทางเพียงครั้งเดียว
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val cold = flow {
println("Upstream started once")
repeat(5) { delay(100); emit(it) }
}
val hot = cold.shareIn(this, SharingStarted.Eagerly, replay = 0)
launch { hot.collect { println("A: $it") } }
launch { hot.collect { println("B: $it") } }
delay(600)
coroutineContext.cancelChildren()
}พื้นฐานของ stateIn
flow.stateIn(scope, started, initialValue) แปลงเป็น StateFlow ซึ่งมีค่าอยู่เสมอและเล่นซ้ำ 1 ค่า
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val cold = flow { delay(100); emit(42) }
val state: StateFlow<Int> = cold.stateIn(
scope = this,
started = SharingStarted.Eagerly,
initialValue = 0
)
println(state.value) // 0 immediately
delay(200)
println(state.value) // 42 after upstream emits
coroutineContext.cancelChildren()
}SharingStarted.Eagerly
Eagerly: ต้นทางจะเริ่มทำงานทันทีเมื่อเรียก shareIn/stateIn โดยไม่ขึ้นอยู่กับจำนวนผู้สมัครรับข้อมูล
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val flow = flow {
println("Eagerly started")
emit(1)
}.shareIn(this, SharingStarted.Eagerly)
// Upstream already running even before any collect
delay(50)
launch { flow.collect { println(it) } }
delay(100)
coroutineContext.cancelChildren()
}SharingStarted.Lazily
Lazily: ต้นทางจะเริ่มทำงานเมื่อมีผู้สมัครรับข้อมูลรายแรก และจะไม่หยุด แม้จำนวนผู้สมัครรับข้อมูลจะลดลงเหลือศูนย์
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val flow = flow {
println("Lazily started on first subscriber")
repeat(3) { delay(100); emit(it) }
}.shareIn(this, SharingStarted.Lazily, replay = 1)
delay(50) // no subscriber yet — not started
launch { flow.collect { println(it) } } // triggers start
delay(400)
coroutineContext.cancelChildren()
}SharingStarted.WhileSubscribed
WhileSubscribed(stopTimeout): ต้นทางจะเริ่มทำงานเมื่อผู้สมัครรับข้อมูลรายแรกเข้ามา และหยุดหลังจากผู้สมัครรับข้อมูลรายสุดท้ายออกไปแล้ว stopTimeout มิลลิวินาที เหมาะอย่างยิ่งสำหรับ ViewModels
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val flow = flow {
println("Started")
repeat(10) { delay(100); emit(it) }
}.shareIn(this, SharingStarted.WhileSubscribed(500))
val job = launch { flow.collect { print("$it ") } }
delay(300)
job.cancel() // subscriber left
delay(200) // within 500ms stop timeout — still running
launch { flow.collect { print("resume $it ") } }
delay(500)
coroutineContext.cancelChildren()
}รูปแบบทั่วไปของ ViewModel
ใน Android ViewModels ให้แปลงโฟลว์จากคลังข้อมูลด้วย stateIn(viewModelScope, WhileSubscribed(5000), initialValue) เพื่อแชร์ข้อมูลและคงการทำงานไว้เมื่อมีการเปลี่ยนแปลงการกำหนดค่า
import kotlinx.coroutines.flow.*
// In ViewModel:
// val uiState: StateFlow<UiState> = repository
// .dataFlow()
// .map { UiState.Success(it) }
// .stateIn(
// scope = viewModelScope,
// started = SharingStarted.WhileSubscribed(5_000),
// initialValue = UiState.Loading
// )
fun main() { println("WhileSubscribed(5000) is the recommended ViewModel pattern") }shareIn กับ stateIn
shareIn → SharedFlow (ไม่มีค่าปัจจุบันและกำหนดการเล่นซ้ำได้) stateIn → StateFlow (มีค่าอยู่เสมอและ replay=1) ให้เลือกตามว่าผู้ใช้ข้อมูลจำเป็นต้องใช้สถานะปัจจุบันหรือไม่
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val cold = (1..3).asFlow().map { it * 10 }
// SharedFlow — no initial value:
val shared: SharedFlow<Int> = cold.shareIn(this, SharingStarted.Eagerly, replay = 1)
// StateFlow — always has a value:
val state: StateFlow<Int> = cold.stateIn(this, SharingStarted.Eagerly, 0)
delay(100)
println("shared cache: ${shared.replayCache}")
println("state value: ${state.value}")
coroutineContext.cancelChildren()
}ข้อแลกเปลี่ยนของแคชการเล่นซ้ำ
การเล่นซ้ำจำนวนมากขึ้นทำให้ผู้สมัครรับข้อมูลที่เข้ามาภายหลังเห็นประวัติมากขึ้น แต่ใช้หน่วยความจำมากขึ้น สำหรับสถานะของส่วนติดต่อผู้ใช้ replay=1 (stateIn) ก็เพียงพอแล้ว ส่วนบันทึกเหตุการณ์อาจต้องใช้การเล่นซ้ำจำนวนมากกว่า
import kotlinx.coroutines.flow.*
// replay=0: no history, only future events
// replay=1: last value (equivalent to stateIn)
// replay=N: last N events — use for message feeds, logs
// Trade-off: memory vs subscriber freshness
fun main() { println("Choose replay based on late-subscriber requirements") }ความเสี่ยงของโฟลว์ที่ไม่ได้แชร์
หากไม่แชร์ข้อมูล ตัวเก็บข้อมูลของ Compose แต่ละตัวหรือผู้สังเกตการณ์ของ ViewModel จะเรียกใช้ต้นทางใหม่ ทำให้เกิดการเรียกเครือข่ายและการค้นหาฐานข้อมูลซ้ำ ควรแชร์โฟลว์ที่ใช้ทรัพยากรมากเสมอ
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun expensiveApi() = flow {
println("API CALL") // without sharing: once per collector
emit("data")
}
fun main() = runBlocking {
val shared = expensiveApi().shareIn(this, SharingStarted.Lazily, replay = 1)
launch { shared.collect { } } // one API call, shared
launch { shared.collect { } } // same emission
delay(200)
coroutineContext.cancelChildren()
}การล้างทรัพยากรเมื่อแชร์ข้อมูล
เมื่อขอบเขตของโฟลว์ที่แชร์ถูกยกเลิก โฟลว์ต้นทางจะถูกยกเลิกด้วย และบล็อก finally ของโฟลว์จะทำงาน ทำให้ทรัพยากรถูกล้างอย่างถูกต้อง
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun resourceFlow() = flow {
try { repeat(10) { delay(100); emit(it) } }
finally { println("Upstream cleaned up") }
}
fun main() = runBlocking {
val scope = CoroutineScope(SupervisorJob())
val shared = resourceFlow().shareIn(scope, SharingStarted.Eagerly)
launch { shared.collect { print("$it ") } }
delay(250)
scope.cancel() // upstream cleanup runs
delay(100)
}ตรวจสอบอย่างรวดเร็ว
กลยุทธ์ SharingStarted ใดที่แนะนำสำหรับ Android ViewModels
ทบทวน
shareIn แปลงเป็น SharedFlow ส่วน stateIn แปลงเป็น StateFlow ให้ใช้ WhileSubscribed ใน ViewModels เพื่อประหยัดทรัพยากร การแชร์ข้อมูลช่วยป้องกันการทำงานของต้นทางซ้ำเมื่อมีตัวเก็บข้อมูลหลายตัวสมัครรับข้อมูล
คำถามที่พบบ่อย
บทเรียน “แปลง Cold Flow เป็น Hot ด้วย shareIn และ stateIn” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “แปลง Cold Flow เป็น Hot ด้วย shareIn และ stateIn” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Kotlin Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Kotlin Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “แปลง Cold Flow เป็น Hot ด้วย shareIn และ stateIn”
แปลง Flow แบบเย็นเป็นสตรีมแบบร้อนด้วยตัวดำเนินการแชร์ คุณปฏิบัติ Kotlin Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Kotlin Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Kotlin Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน
บทเรียน “แปลง Cold Flow เป็น Hot ด้วย shareIn และ stateIn” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Kotlin Academy นี้ได้ไหม
ได้ บทเรียน Kotlin Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- StateFlow: ตัวเก็บสถานะแบบร้อนสำหรับ UI
- SharedFlow: บัสเหตุการณ์และเหตุการณ์แบบครั้งเดียว
- แปลง Cold Flow เป็น Hot ด้วย shareIn และ stateIn
- ทดสอบ StateFlow และ SharedFlow ด้วย Turbine