You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何实现应用后台/锁屏时保持定位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

火山引擎 最新活动