如何在Android Activity间共享Observable数据流(Flow)避免重复请求
解决方案:跨Activity保留视频链接获取任务,避免重复执行
针对你遇到的「跳转Activity后获取任务被取消、不想用Service」的问题,推荐以下几个轻量且符合Jetpack最佳实践的方案:
方案1:用Application级CoroutineScope托管异步任务
ViewModel的viewModelScope会随Activity销毁而自动取消协程,而Application级别的Scope生命周期与APP一致,能保证跳转Activity后任务继续执行。
步骤1:创建Application级Scope
class MyApp : Application() { // 用SupervisorJob确保单个任务失败不影响其他任务 val appScope = CoroutineScope(SupervisorJob() + Dispatchers.IO) }
步骤2:改造UseCase,共享加载状态和结果
class SampleUseCase @Inject constructor( private val app: MyApp ) { private val _loadedLinks = mutableListOf<String>() private val _linksFlow = MutableSharedFlow<String>(replay = Int.MAX_VALUE) private var isLoading = false // 对外暴露已加载的链接和实时更新的Flow val loadedLinks: List<String> get() = _loadedLinks.toList() val linksFlow: Flow<String> get() = _linksFlow suspend fun loadLinks() { if (isLoading) return // 避免重复触发加载 isLoading = true app.appScope.launch { // 模拟逐个获取视频链接的耗时逻辑 val videoIds = listOf("vid_01", "vid_02", "vid_03") videoIds.forEach { id -> val link = fetchSingleVideoLink(id) _loadedLinks.add(link) _linksFlow.emit(link) } isLoading = false } } // 模拟实际的链接获取(网络请求/本地解析等) private suspend fun fetchSingleVideoLink(videoId: String): String { delay(1200) // 模拟耗时 return "https://example.com/videos/$videoId.mp4" } }
步骤3:HomeActivity启动加载
你的原有HomeActivity代码基本不用改,ViewModel调用UseCase的loadLinks()即可:
class MyVM @Inject constructor( val usecase: SampleUseCase ) : ViewModel() { fun startLinkFetch() { viewModelScope.launch { usecase.loadLinks() } } } class HomeActivity : ComponentActivity() { val viewModel: MyVM by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewModel.startLinkFetch() setContent { Button(onClick = { startActivity(Intent(this, PlayerActivity::class.java)) }) { Text("跳转播放页") } } } }
步骤4:PlayerActivity接收实时结果
class PlayerActivity : ComponentActivity() { @Inject lateinit var usecase: SampleUseCase override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 初始化依赖注入(比如Hilt) // 先加载已经获取到的链接 val preLoadedLinks = usecase.loadedLinks initPlaylist(preLoadedLinks) // 监听后续新获取的链接 lifecycleScope.launch { usecase.linksFlow.collect { newLink -> addToPlaylist(newLink) } } } private fun initPlaylist(links: List<String>) { /* 初始化播放列表 */ } private fun addToPlaylist(link: String) { /* 添加新链接到播放列表 */ } }
方案2:用WorkManager执行后台获取(适合需后台持续执行的场景)
如果你的链接获取需要在APP退到后台也能继续,WorkManager是比Service更轻量的选择,它会自动处理系统的后台限制:
class LinkFetchWorker( context: Context, params: WorkerParameters ) : CoroutineWorker(context, params) { @Inject lateinit var usecase: SampleUseCase override suspend fun doWork(): Result { return try { usecase.loadLinks() Result.success() } catch (e: Exception) { Result.retry() } } } // 在HomeActivity中启动Worker val workRequest = OneTimeWorkRequestBuilder<LinkFetchWorker>().build() WorkManager.getInstance(this).enqueue(workRequest)
关键注意点
- 用
SupervisorJob替代默认Job:确保单个链接获取失败不会中断整个任务队列 SharedFlow的replay参数:设置为Int.MAX_VALUE可以让PlayerActivity启动后收到之前所有已加载的链接- 避免重复加载:在UseCase中加入
isLoading标记,防止多次调用loadLinks()触发重复任务
内容的提问来源于stack exchange,提问作者Firate
相关产品推荐
相关产品推荐

