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

已获取定位权限却仅能重启后获取当前位置的问题排查

首次授权定位权限后无法获取位置,重启应用才生效的解决方案

我希望在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) }
    }
}

问题原因

  1. 权限授权前已初始化Flow:应用启动时LocationDataSource的locationSource就已创建并发起位置请求,但此时权限未授权,FusedLocationProviderClient不会触发onLocationResult回调。而Flow一旦创建不会自动重新发起请求,导致授权后无新位置事件。
  2. 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重启请求:

  1. 修改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)
    }
}
  1. 在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 05:45:32