已获取定位权限却仅能重启后获取当前位置的问题排查
首次授权定位权限后无法获取位置,重启应用才生效的解决方案
我希望在ViewModel中获取当前位置并执行操作,但首次启动应用并授权定位权限后,没有日志输出,只有重启应用后才能看到位置日志。相关代码如下:
ViewModel初始化代码
init { viewModelScope.launch { locationRepository.location.collect { Log.d(TAG, it.toString()) } } }
LocationRepository实现
class LocationRepositoryImpl @Inject constructor( private val dataSource: LocationDataSource, @ApplicationScope private val externalScope: CoroutineScope ) : LocationRepository { override val location: Flow<MapLocation> = dataSource.locationSource .shareIn( scope = externalScope, started = WhileSubscribed() ) }
LocationDataSource实现
class LocationDataSource @Inject constructor( private val client: FusedLocationProviderClient ) { val locationSource: Flow<MapLocation> = callbackFlow { val request = LocationRequest.create().apply { interval = TimeUnit.SECONDS.toMillis(4) fastestInterval = TimeUnit.SECONDS.toMillis(4) priority = Priority.PRIORITY_HIGH_ACCURACY } val callBack = object : LocationCallback() { override fun onLocationResult(result: LocationResult) { super.onLocationResult(result) result.lastLocation?.let { trySend(it.asModel()) } } } //Subscribe to location changes. client.requestLocationUpdates(request, callBack, Looper.getMainLooper()) awaitClose { client.removeLocationUpdates(callBack) } } }
问题原因
- 权限授权前已初始化Flow:应用启动时
LocationDataSource的locationSource就已创建并发起位置请求,但此时权限未授权,FusedLocationProviderClient不会触发onLocationResult回调。而Flow一旦创建不会自动重新发起请求,导致授权后无新位置事件。 - ShareIn启动策略(次要):
WhileSubscribed()默认在无订阅者时立即停止流,若后续出现订阅者短暂断开,可能导致流无法恢复。
解决方案
方案1:监听权限变化,动态重启位置请求
修改LocationDataSource,添加权限监听逻辑,当定位权限授权后自动重新发起请求:
class LocationDataSource @Inject constructor( private val client: FusedLocationProviderClient, private val context: Context ) { private val locationPermissionState = MutableStateFlow(false) init { // 监听权限变化 CoroutineScope(Dispatchers.IO).launch { while (true) { val hasPermission = ContextCompat.checkSelfPermission( context, Manifest.permission.ACCESS_FINE_LOCATION ) == PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission( context, Manifest.permission.ACCESS_COARSE_LOCATION ) == PackageManager.PERMISSION_GRANTED locationPermissionState.value = hasPermission delay(1000) // 定期检查权限状态 } } } val locationSource: Flow<MapLocation> = locationPermissionState .flatMapLatest { hasPermission -> if (hasPermission) { callbackFlow { val request = LocationRequest.create().apply { interval = TimeUnit.SECONDS.toMillis(4) fastestInterval = TimeUnit.SECONDS.toMillis(4) priority = Priority.PRIORITY_HIGH_ACCURACY } val callBack = object : LocationCallback() { override fun onLocationResult(result: LocationResult) { super.onLocationResult(result) result.lastLocation?.let { trySend(it.asModel()) } } } client.requestLocationUpdates(request, callBack, Looper.getMainLooper()) awaitClose { client.removeLocationUpdates(callBack) } } } else { emptyFlow() } } }
方案2:权限请求成功后手动触发位置请求
在Activity/Fragment中处理权限请求,授权成功后通知LocationDataSource重启请求:
- 修改
LocationDataSource添加触发方法:
class LocationDataSource @Inject constructor( private val client: FusedLocationProviderClient ) { private val requestTrigger = MutableSharedFlow<Unit>(replay = 1) val locationSource: Flow<MapLocation> = requestTrigger .flatMapLatest { callbackFlow { val request = LocationRequest.create().apply { interval = TimeUnit.SECONDS.toMillis(4) fastestInterval = TimeUnit.SECONDS.toMillis(4) priority = Priority.PRIORITY_HIGH_ACCURACY } val callBack = object : LocationCallback() { override fun onLocationResult(result: LocationResult) { super.onLocationResult(result) result.lastLocation?.let { trySend(it.asModel()) } } } client.requestLocationUpdates(request, callBack, Looper.getMainLooper()) awaitClose { client.removeLocationUpdates(callBack) } } } fun restartLocationRequest() { requestTrigger.tryEmit(Unit) } }
- 在Activity中处理权限请求:
class MainActivity : AppCompatActivity() { private val locationPermissionRequest = registerForActivityResult( ActivityResultContracts.RequestMultiplePermissions() ) { permissions -> if (permissions[Manifest.permission.ACCESS_FINE_LOCATION] == true && permissions[Manifest.permission.ACCESS_COARSE_LOCATION] == true ) { locationDataSource.restartLocationRequest() } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val hasFineLocation = ContextCompat.checkSelfPermission( this, Manifest.permission.ACCESS_FINE_LOCATION ) == PackageManager.PERMISSION_GRANTED val hasCoarseLocation = ContextCompat.checkSelfPermission( this, Manifest.permission.ACCESS_COARSE_LOCATION ) == PackageManager.PERMISSION_GRANTED if (!hasFineLocation || !hasCoarseLocation) { locationPermissionRequest.launch( arrayOf( Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION ) ) } else { locationDataSource.restartLocationRequest() } } }
方案3:优化ShareIn启动策略(辅助)
将shareIn的started参数改为WhileSubscribed(5000L),避免短暂订阅暂停导致流停止:
override val location: Flow<MapLocation> = dataSource.locationSource .shareIn( scope = externalScope, started = WhileSubscribed(5000L) // 延迟5秒后停止流 )
内容的提问来源于stack exchange,提问作者Nero Nguyen
相关产品推荐
相关产品推荐

