Kotlin Flows如何在首订阅和末退订时自动执行位置更新启停逻辑
解决方案
可以直接使用Kotlin协程Flow自带的shareIn操作符配合SharingStarted.WhileSubscribed()实现需求,完全不需要手动维护订阅计数器,系统实现已经内置了订阅数自动统计逻辑,比手动计数更稳定,也能避免并发场景下的计数异常问题。
实现步骤
- 第一步:将FusedLocationProvider的位置回调封装为
callbackFlow,冷流特性可以保证只有订阅时才会启动位置更新,流取消时自动停止更新 - 第二步:使用
shareIn操作符将冷流转换为共享热流,指定SharingStarted.WhileSubscribed()作为启动策略,即可实现「首个订阅者出现时启动上游流,最后一个订阅者退出时关闭上游流」的逻辑 - 可配置
WhileSubscribed的stopTimeoutMillis参数,设置最后一个订阅者退出后延迟停止位置更新的时长,避免页面横竖屏切换等短时间退订再订阅的场景触发频繁启停
优化后完整代码
@Singleton class LocationRepository @Inject constructor( app: Application ) { // 绑定仓库生命周期的协程作用域,单例场景下和App生命周期一致 private val repositoryScope = CoroutineScope(SupervisorJob() + Dispatchers.Default) private val client = LocationServices.getFusedLocationProviderClient(app) private val request = LocationRequest.create().apply { priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY interval = 5000L fastestInterval = 2500L } val location: Flow<Location?> = callbackFlow { // 位置回调实现 val locCb = object : LocationCallback() { override fun onLocationResult(var1: LocationResult?) { var1?.lastLocation?.let { trySend(it) } } override fun onLocationAvailability(var1: LocationAvailability) {} } // 启动位置更新 client.requestLocationUpdates(request, locCb, Looper.getMainLooper()) // 流取消时自动移除回调、停止更新 awaitClose { client.removeLocationUpdates(locCb) } } // 配置共享流策略 .shareIn( scope = repositoryScope, // 最后一个订阅者退出后延迟3s再停止,避免频繁启停 started = SharingStarted.WhileSubscribed(stopTimeoutMillis = 3000), // 新订阅者可以拿到最新的1条位置数据,和原StateFlow行为一致 replay = 1 ) }
方案说明
- 对外暴露的
location流接口和原有实现完全一致,调用侧不需要做任何修改 - 不需要手动维护
AtomicInteger计数,也不需要额外配置onSubscription/onCompletion回调,所有订阅管理逻辑由系统内置实现处理 callbackFlow的awaitClose块可以保证位置回调一定会被移除,不会出现内存泄漏问题
如果你的需求必须保留原有MutableStateFlow的实现,也可以直接使用stateIn操作符替代手动维护的_location变量,stateIn同样支持SharingStarted.WhileSubscribed()策略,逻辑和上述shareIn完全一致。
内容的提问来源于stack exchange,提问作者Cruces
相关产品推荐
相关产品推荐

