You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 15:52:39