如何使用Kotlin在后台获取Android设备的经纬度?
Kotlin 实现 Android 后台经纬度检测方案
1. 前置配置:声明权限
首先在AndroidManifest.xml中添加所需定位权限,根据你的定位精度需求和目标系统版本选择:
<!-- 基础定位权限 --> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- 网络定位精度,误差百米级 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- GPS定位精度,误差十米级 --> <!-- 后台定位权限(Android 10及以上需要) --> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> <!-- 网络权限,用于后续拉取天气数据 --> <uses-permission android:name="android.permission.INTERNET" />
如果选择Google Play服务的融合定位SDK(推荐,功耗更低、定位精度更高),需要在模块级build.gradle中添加依赖:
dependencies { implementation "com.google.android.gms:play-services-location:21.0.1" }
2. 动态申请权限
定位属于危险权限,必须在运行时动态申请,推荐使用Activity Result API实现:
// 注册权限申请回调 val requestLocationPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> val fineLocationGranted = permissions[Manifest.permission.ACCESS_FINE_LOCATION] ?: false val coarseLocationGranted = permissions[Manifest.permission.ACCESS_COARSE_LOCATION] ?: false val backgroundLocationGranted = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { permissions[Manifest.permission.ACCESS_BACKGROUND_LOCATION] ?: false } else true when { fineLocationGranted || coarseLocationGranted -> { // 基础定位权限已获取,可以开始定位 getCurrentLocation() } else -> { // 权限被拒绝,引导用户到设置页开启 } } } // 触发权限申请 fun requestLocationPermissions() { val permissionList = mutableListOf( Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION ) // Android 10及以上追加后台定位权限申请 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { permissionList.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION) } requestLocationPermissionLauncher.launch(permissionList.toTypedArray()) }
3. 核心定位逻辑实现
单次定位获取(适合启动时拉取当前位置天气)
private lateinit var fusedLocationClient: FusedLocationProviderClient override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) fusedLocationClient = LocationServices.getFusedLocationProviderClient(this) } @SuppressLint("MissingPermission") fun getCurrentLocation() { fusedLocationClient.lastLocation .addOnSuccessListener { location: Location? -> location?.let { val latitude = it.latitude val longitude = it.longitude // 拿到经纬度后调用天气接口拉取数据 fetchWeatherData(latitude, longitude) } ?: run { // 历史位置为空,主动请求一次新鲜定位 val locationRequest = LocationRequest.create().apply { priority = LocationRequest.PRIORITY_HIGH_ACCURACY interval = 10000 fastestInterval = 5000 numUpdates = 1 } fusedLocationClient.requestLocationUpdates( locationRequest, object : LocationCallback() { override fun onLocationResult(locationResult: LocationResult) { super.onLocationResult(locationResult) val newLocation = locationResult.lastLocation ?: return val latitude = newLocation.latitude val longitude = newLocation.longitude fetchWeatherData(latitude, longitude) fusedLocationClient.removeLocationUpdates(this) } }, Looper.getMainLooper() ) } } }
后台定时定位(适合每隔几小时更新一次位置天气)
推荐使用WorkManager实现后台定时任务,避免被系统后台策略杀死:
// 定义定位Worker class LocationWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) { override suspend fun doWork(): Result { val fusedLocationClient = LocationServices.getFusedLocationProviderClient(applicationContext) return try { val location = Tasks.await(fusedLocationClient.lastLocation) location?.let { // 这里执行天气数据拉取逻辑,可以存到本地SP或者数据库 saveLocationAndFetchWeather(it.latitude, it.longitude) } Result.success() } catch (e: Exception) { Result.retry() } } } // 注册定时任务,每隔6小时执行一次 fun schedulePeriodicLocationCheck() { val periodicWorkRequest = PeriodicWorkRequestBuilder<LocationWorker>(6, TimeUnit.HOURS) .setConstraints( Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .build() ) .build() WorkManager.getInstance(context).enqueueUniquePeriodicWork( "location_weather_update", ExistingPeriodicWorkPolicy.KEEP, periodicWorkRequest ) }
注意事项
- 若用户拒绝后台定位权限,只能在app前台运行时获取位置,需要做好降级处理
- 不要设置过高的定位频率,避免不必要的功耗消耗,天气类应用2-6小时更新一次位置足够
- 国内部分厂商系统有自定义后台权限限制,需要适配厂商的权限白名单引导逻辑
内容的提问来源于stack exchange,提问作者android coder
相关产品推荐
相关产品推荐

