权限授予后立即获取Android设备定位的问题排查
问题分析与解决方案
你的核心问题有两个:一是过时的onRequestPermissionsResult无法触发回调,二是getLocation的同步逻辑无法获取异步返回的定位结果,导致权限授予后立刻拿不到坐标,必须重启Fragment依赖缓存的lastKnownLocation。
1. 替换过时的权限请求方式(用Activity Result API)
onRequestPermissionsResult已被官方废弃,在Fragment配合导航组件时,旧方法常因生命周期问题无法触发回调。改用Activity Result API是官方推荐的可靠方案:
步骤1:初始化权限请求Launcher
在Fragment的onViewCreated中添加:
private val locationPermissionLauncher = registerForActivityResult( ActivityResultContracts.RequestMultiplePermissions() ) { permissions -> val isAllGranted = permissions.values.all { it } if (isAllGranted) { getLocation() } else { Toast.makeText(requireContext(), "需授予定位权限才能获取位置", Toast.LENGTH_SHORT).show() } }
步骤2:修改requestPermissions函数
替换原来的权限请求逻辑:
fun requestPermissions() { locationPermissionLauncher.launch( arrayOf( android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_COARSE_LOCATION ) ) }
步骤3:删除过时的onRequestPermissionsResult
直接移除原来的onRequestPermissionsResult函数,无需保留。
2. 修复getLocation的异步逻辑
requestLocationUpdates是异步操作,你原来的代码在调用后立刻读取locationByGps/locationByNetwork,此时回调尚未触发,必然拿到空值。需要在定位回调中处理坐标更新,并完成后再执行导航:
步骤1:定义定位完成回调
private fun interface OnLocationReady { fun onReady(latitude: Double, longitude: Double) }
步骤2:重写getLocation函数
fun getLocation(onLocationReady: OnLocationReady) { if (checkPermissions()) { if (isLocationEnabled()) { var hasReceivedLocation = false val locationListener = LocationListener { location -> if (!hasReceivedLocation) { hasReceivedLocation = true // 拿到定位后立刻移除更新,避免重复回调 locationManager.removeUpdates(locationListener) // 通知定位完成 onLocationReady.onReady(location.latitude, location.longitude) } } // 注册GPS和网络定位回调,指定主线程处理 locationManager.requestLocationUpdates( LocationManager.GPS_PROVIDER, 0, 0f, locationListener, Looper.getMainLooper() ) locationManager.requestLocationUpdates( LocationManager.NETWORK_PROVIDER, 0, 0f, locationListener, Looper.getMainLooper() ) // 优先使用最后已知位置,避免等待过久 val lastKnownLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER) ?: locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER) lastKnownLocation?.let { if (!hasReceivedLocation) { hasReceivedLocation = true onLocationReady.onReady(it.latitude, it.longitude) } } } else { Toast.makeText(requireContext(), "请打开定位服务", Toast.LENGTH_LONG).show() startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)) } } else { requestPermissions() } }
步骤3:修改按钮点击事件
等待定位完成后再更新模型并导航:
currentlocalisation.setOnClickListener { Toast.makeText( requireActivity(), "请将手机左右移动画八字形以加快定位", Toast.LENGTH_LONG ).show() getLocation(object : OnLocationReady { override fun onReady(latitude: Double, longitude: Double) { model.modifyCoordinatesLocation(longitude, latitude) findNavController().navigate(R.id.toPostel) } }) }
3. 优化:避免内存泄漏
在Fragment销毁时移除定位更新请求,防止内存泄漏:
override fun onDestroyView() { super.onDestroyView() // 注意:需在getLocation中持有locationListener的引用,才能在这里移除 locationManager.removeUpdates(locationListener) }
为什么原来的代码不生效?
- 过时的权限回调:
onRequestPermissionsResult在导航组件管理的Fragment中,常会因Fragment被重新创建而无法触发回调,Activity Result API解决了这个问题。 - 同步逻辑错误:
requestLocationUpdates的回调是异步触发的,你原来的代码在调用后立刻读取位置变量,此时回调还未执行,必然拿到空值;重启Fragment后能工作,是因为读取了缓存的lastKnownLocation,但权限刚授予时该缓存可能不存在。 - 提前导航:按钮点击后立刻执行导航,此时定位逻辑还未完成,模型中的坐标还是旧值。
内容的提问来源于stack exchange,提问作者gunsailor
相关产品推荐
相关产品推荐

