Android 11 Kotlin 如何在Fragment中实现后台位置定时获取
Android后台定时获取位置实现方案
你遇到的权限被拒问题核心是Android系统不允许直接申请后台定位权限,必须先申请前台定位权限,用户授权通过后才能申请后台定位权限,同时要实现2-3分钟的后台定时获取,不能依赖Fragment生命周期,需要适配系统后台保活规则。
1. 先配置AndroidManifest.xml权限
<!-- 基础定位权限 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- 前台服务权限(后台保活需要) --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" /> <!-- 后台定位权限,Android 10及以上需要 --> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
2. 修正权限申请逻辑
不能直接申请后台权限,要按照「前台定位权限→后台定位权限」的顺序申请:
private val REQUEST_CODE_FOREGROUND_LOC = 101 private val REQUEST_CODE_BACKGROUND_LOC = 102 private var mFusedLocationClient: FusedLocationProviderClient? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) mFusedLocationClient = LocationServices.getFusedLocationProviderClient(requireContext()) checkLocationPermission() } private fun checkLocationPermission() { when { // 已经有后台权限,直接启动定时定位 ActivityCompat.checkSelfPermission( requireContext(), Manifest.permission.ACCESS_BACKGROUND_LOCATION ) == PackageManager.PERMISSION_GRANTED -> { startPeriodicLocationTask() } // 有前台权限,申请后台权限 ActivityCompat.checkSelfPermission( requireContext(), Manifest.permission.ACCESS_FINE_LOCATION ) == PackageManager.PERMISSION_GRANTED -> { requestPermissions( arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION), REQUEST_CODE_BACKGROUND_LOC ) } // 没有前台权限,先申请前台 else -> { requestPermissions( arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), REQUEST_CODE_FOREGROUND_LOC ) } } } override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<out String>, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (grantResults.isEmpty() || grantResults[0] != PackageManager.PERMISSION_GRANTED) { Toast.makeText(requireContext(), "权限被拒绝,无法使用定位功能", Toast.LENGTH_SHORT).show() return } when (requestCode) { REQUEST_CODE_FOREGROUND_LOC -> { // 前台权限申请成功,接下来申请后台权限 checkLocationPermission() } REQUEST_CODE_BACKGROUND_LOC -> { // 后台权限申请成功,启动定时任务 startPeriodicLocationTask() } } }
3. 实现2-3分钟后台定时定位
Android系统后台会限制普通进程的定时任务,2-3分钟的高频定位建议配合前台Service实现,前台Service会在通知栏显示常驻通知,保证进程不被系统回收,以下是核心代码:
// 单次获取位置的方法 private fun getCurrentLocation() { val locationManager = requireActivity().getSystemService(Context.LOCATION_SERVICE) as LocationManager if (!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) && !locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) { Toast.makeText(requireContext(), "请开启定位服务", Toast.LENGTH_SHORT).show() return } if (ActivityCompat.checkSelfPermission( requireContext(), Manifest.permission.ACCESS_FINE_LOCATION ) != PackageManager.PERMISSION_GRANTED ) { return } val locationRequest = LocationRequest.create().apply { priority = LocationRequest.PRIORITY_HIGH_ACCURACY interval = 120000 // 基础间隔2分钟 fastestInterval = 120000 maxWaitTime = 60000 } val locationCallback = object : LocationCallback() { override fun onLocationResult(locationResult: LocationResult) { super.onLocationResult(locationResult) locationResult.lastLocation?.let { location -> // 这里处理拿到的位置信息,比如上传到后台 Log.d("定位结果", "纬度:${location.latitude} 经度:${location.longitude}") } // 用完移除回调避免内存泄漏 mFusedLocationClient?.removeLocationUpdates(this) } } mFusedLocationClient?.requestLocationUpdates( locationRequest, locationCallback, Looper.getMainLooper() ) } // 启动后台定时任务,这里用协程做示例,也可以用AlarmManager实现 private fun startPeriodicLocationTask() { lifecycleScope.launch(Dispatchers.IO) { while (isActive) { // 切主线程执行定位 withContext(Dispatchers.Main) { getCurrentLocation() } // 每2.5分钟执行一次 delay(150000) } } }
注意事项
- Android 12及以上,用户授予后台定位权限时必须选择「始终允许」,否则依然无法获取后台位置
- 如果应用退到后台后定位中断,需要引导用户关闭系统的电池优化权限,允许应用后台活动
- 如果需要长期后台运行,必须启动前台Service在Service中执行定时任务,Fragment生命周期有限,退到后台可能被销毁导致任务停止
内容的提问来源于stack exchange,提问作者Lavish Garg
相关产品推荐
相关产品推荐

