Android后台GPS服务使用:未启动APP时触发距离通知
解决Android后台位置监测与通知推送问题
嘿,作为Android新手碰到这种后台功能的问题太正常了——毕竟Android为了省电和性能,对后台运行的限制挺严格的。不过别担心,咱们可以通过几个核心步骤实现你要的功能,下面给你详细拆解:
1. 先搞定必要的权限
首先得让APP有获取位置和推送通知的权限,在AndroidManifest.xml里加上这些:
<!-- 精准/粗略位置权限 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- 后台位置权限(Android 10+ 必须) --> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> <!-- Android 13+ 推送通知需要单独权限 --> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
⚠️ 这些都是危险权限,一定要在APP运行时动态申请,而且得给用户讲清楚为什么需要这些权限(比如“需要后台监测位置来提醒您到达目标区域”),不然用户大概率会拒绝。
2. 用前台服务保持位置监听存活
Android 8.0之后,普通后台服务很容易被系统杀死,所以咱们得用前台服务——它会在状态栏显示一个通知,告诉用户APP正在后台运行,这样系统就不会轻易终止它。
核心代码示例(Kotlin)
创建一个继承Service的位置监测服务:
class LocationMonitorService : Service() { private lateinit var fusedLocationClient: FusedLocationProviderClient // 目标位置,你可以提前配置或者从本地存储读取 private val targetLocation = Location("").apply { latitude = 39.9042 // 示例纬度 longitude = 116.4074 // 示例经度 } // 位置更新回调 private val locationCallback = object : LocationCallback() { override fun onLocationResult(locationResult: LocationResult) { locationResult.lastLocation?.let { currentLoc -> // 调用你已经掌握的距离计算方法 val distanceInMeters = currentLoc.distanceTo(targetLocation) if (distanceInMeters < 1000) { // 小于1公里时推送通知 sendArrivalNotification() } } } } override fun onCreate() { super.onCreate() fusedLocationClient = LocationServices.getFusedLocationProviderClient(this) } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { // 创建前台通知,必须显示这个才能让服务在前台运行 val foregroundNotification = NotificationCompat.Builder(this, "LOCATION_MONITOR_CHANNEL") .setContentTitle("位置监测中") .setContentText("后台正在帮你留意目标区域") .setSmallIcon(R.drawable.ic_location) .build() // 启动前台服务,传入通知ID和通知实例 startForeground(1001, foregroundNotification) // 配置位置更新请求 val locationRequest = LocationRequest.create().apply { interval = 60000 // 1分钟更新一次位置,可根据需求调整 fastestInterval = 30000 priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY // 平衡精度和耗电 } // 开始监听位置更新 fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper()) return START_STICKY // 服务被杀死后尝试重启 } // 推送到达通知的方法 private fun sendArrivalNotification() { val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager // Android 8.0+ 需要先创建通知渠道 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val alertChannel = NotificationChannel( "ARRIVAL_ALERT_CHANNEL", "到达提醒", NotificationManager.IMPORTANCE_HIGH ) notificationManager.createNotificationChannel(alertChannel) } val alertNotification = NotificationCompat.Builder(this, "ARRIVAL_ALERT_CHANNEL") .setContentTitle("已到达目标区域!") .setContentText("你离设定的位置已经不足1公里啦") .setSmallIcon(R.drawable.ic_alert) .setPriority(NotificationCompat.PRIORITY_HIGH) .build() notificationManager.notify(1002, alertNotification) } override fun onBind(intent: Intent?): IBinder? = null override fun onDestroy() { super.onDestroy() // 停止位置监听,避免内存泄漏 fusedLocationClient.removeLocationUpdates(locationCallback) } }
3. 启动服务的正确姿势
- 用户第一次打开APP并授权后,就可以启动服务:
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED) { startService(Intent(this, LocationMonitorService::class.java)) } - 如果希望设备重启后服务自动启动,可以注册
BOOT_COMPLETED广播接收器,但注意Android 10+之后广播的触发条件变严,可能需要结合WorkManager来兜底。
4. 优化小技巧
- 调整位置更新间隔:别太频繁,比如1-5分钟一次就够了,不然耗电会很严重
- 用WorkManager做周期性检查:如果不需要实时监听,只是每隔一段时间检查一次位置,可以用WorkManager来调度,这样更省电,系统也会更友好
- 适配厂商省电策略:国内很多厂商有自己的省电模式,会限制后台服务,最好在APP里加个引导,告诉用户把APP加入省电白名单
内容的提问来源于stack exchange,提问作者Jamal Alkelani
相关产品推荐
相关产品推荐

