如何实现应用后台/锁屏时保持定位Service运行,划掉应用时停止
我来帮你解决这个后台定位的问题!你的核心需求是让应用在后台/锁屏时持续定位,同时在最近列表划掉后彻底停止服务,这完全可以通过前台服务+任务移除回调来实现,和Google Maps的逻辑对齐。
核心问题分析
Android 8.0(API 26)及以上系统对后台服务有严格限制,普通Service在应用进入后台后会被系统快速回收,所以必须使用startForegroundService将服务提升为前台级别,同时强制显示通知(这是系统要求)。但默认前台服务在应用被划掉后不会自动停止,需要我们手动处理任务移除的回调。
解决方案步骤
1. 配置Manifest权限与Service声明
首先更新AndroidManifest.xml,添加前台服务权限,并配置Service的基础属性:
<!-- 前台服务基础权限(API26+) --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <!-- Android 10+ 额外需要定位类前台服务权限 --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" /> <service android:name=".logic.service.LocationService" android:stopWithTask="false" /> <!-- 手动处理任务移除逻辑,设为false -->
2. 将Service转为前台服务并显示通知
在LocationService中创建前台通知(Android 8.0+需要先创建通知渠道),并在onStartCommand中启动前台服务:
public class LocationService extends Service { private static final int NOTIFICATION_ID = 1001; private static final String CHANNEL_ID = "LOCATION_TRACKER_CHANNEL"; private static final String CHANNEL_NAME = "定位追踪"; private FusedLocationProviderClient fusedLocationProviderClient; private LocationRequest locationRequest; private LocationCallback locationCallback; private static final long LOCATION_REQUEST_INTERVAL = 5000; private static final long LOCATION_REQUEST_FASTEST_INTERVAL = 2000; @Override public void onCreate() { super.onCreate(); // 为API26+创建通知渠道 createNotificationChannel(); } @Override public int onStartCommand(final Intent intent, final int flags, final int startId) { // 处理手动停止服务的意图 if ("STOP_LOCATION_SERVICE".equals(intent.getAction())) { stopSelf(); return START_NOT_STICKY; } // 启动前台服务并绑定通知 startForeground(NOTIFICATION_ID, buildNotification()); startFusedLocationProviderClient(); // 使用START_NOT_STICKY:服务销毁后不自动重启,符合划掉应用停止的需求 return START_NOT_STICKY; } // 创建通知渠道(API26+必备) private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW); channel.setDescription("应用正在后台追踪位置"); NotificationManager manager = getSystemService(NotificationManager.class); manager.createNotificationChannel(channel); } } // 构建带关闭按钮的前台通知 private Notification buildNotification() { // 停止服务的意图 Intent stopIntent = new Intent(this, LocationService.class); stopIntent.setAction("STOP_LOCATION_SERVICE"); PendingIntent stopPendingIntent = PendingIntent.getService(this, 0, stopIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); return new NotificationCompat.Builder(this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_location) // 替换为你的应用图标 .setContentTitle("位置追踪中") .setContentText("点击关闭追踪") .setPriority(NotificationCompat.PRIORITY_LOW) .addAction(R.drawable.ic_close, "停止追踪", stopPendingIntent) // 添加关闭按钮 .build(); } // ... 原有startFusedLocationProviderClient、getCurrentLocation等方法保持不变 ... }
3. 处理任务移除与资源清理
重写onTaskRemoved方法(用户从最近列表划掉应用时触发),并完善onDestroy的资源清理逻辑:
@Override public void onTaskRemoved(Intent rootIntent) { // 应用被划掉时,停止定位并销毁服务 stopLocationUpdates(); stopSelf(); super.onTaskRemoved(rootIntent); } @Override public void onDestroy() { // 服务销毁时彻底清理定位资源 stopLocationUpdates(); super.onDestroy(); } // 封装停止定位更新的工具方法 private void stopLocationUpdates() { if (fusedLocationProviderClient != null && locationCallback != null) { fusedLocationProviderClient.removeLocationUpdates(locationCallback); } }
4. 修改Activity中的服务启动逻辑
在MainActivity中兼容不同版本的服务启动方式:
if (checkNeedLocationPermission(this)) { Intent serviceIntent = new Intent(getBaseContext(), LocationService.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForegroundService(serviceIntent); } else { startService(serviceIntent); } }
关键说明
- 前台服务强制通知:Android 8.0+要求前台服务必须显示通知,这是系统规则,和Google Maps的逻辑完全一致。
- START_NOT_STICKY:替换原来的
START_STICKY,确保服务被销毁后不会自动重启,符合划掉应用就停止的需求。 - onTaskRemoved:这是处理最近列表划掉应用的核心回调,能确保应用被彻底关闭时停止所有定位操作。
- 通知关闭按钮:给用户提供手动停止的入口,和主流地图应用的交互体验对齐。
这样修改后,你的应用在后台/锁屏时会持续接收定位更新,前台显示通知;当用户划掉应用或点击通知的停止按钮时,定位会立即停止,服务也会被销毁。
内容的提问来源于stack exchange,提问作者RayzaN




