You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过AlarmManager实现每10分钟单次获取用户位置(非持续监听)

解决每10分钟单次获取后台位置的问题

之前用AlarmService启动LocationCheckService导致持续监听位置,确实会浪费电量和系统资源。我给你两种可行的方案,优先推荐官方的WorkManager,它能更好地适配Android各个版本的后台限制,即使应用退出(非强制停止)也能稳定执行任务。

方案一:WorkManager(官方推荐)

WorkManager是专门为后台延迟/周期性任务设计的组件,能自动处理系统休眠、后台限制等问题,不用自己操心复杂的版本适配细节。

1. 添加依赖

在你的build.gradle(app)文件中加入WorkManager的依赖:

dependencies {
    def work_version = "2.8.1"
    implementation "androidx.work:work-runtime:$work_version"
}

2. 编写位置获取Worker类

这个Worker只执行单次位置请求,拿到位置后就结束任务,不会持续监听:

public class LocationFetchWorker extends Worker {
    private FusedLocationProviderClient fusedLocationClient;

    public LocationFetchWorker(@NonNull Context context, @NonNull WorkerParameters params) {
        super(context, params);
        fusedLocationClient = LocationServices.getFusedLocationProviderClient(context);
    }

    @NonNull
    @Override
    public Result doWork() {
        // 先检查位置权限,无权限直接返回失败
        if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
                ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            return Result.failure();
        }

        // 发起单次位置请求,平衡精度与电量消耗
        fusedLocationClient.getCurrentLocation(Priority.PRIORITY_BALANCED_POWER_ACCURACY, null)
                .addOnSuccessListener(location -> {
                    if (location != null) {
                        // 这里处理你的位置业务逻辑:比如保存到本地、上传服务器
                        double latitude = location.getLatitude();
                        double longitude = location.getLongitude();
                        // TODO: 替换为你的实际业务代码
                    }
                })
                .addOnFailureListener(e -> {
                    Log.e("LocationWorker", "获取位置失败: " + e.getMessage());
                });

        // 无论位置是否获取成功,Worker任务都视为完成
        return Result.success();
    }
}

3. 调度周期性任务

在MainActivity的onCreate方法中配置每10分钟执行一次任务:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // 配置每10分钟执行一次,预留5分钟弹性窗口让系统优化电池使用
    PeriodicWorkRequest locationTask = new PeriodicWorkRequest.Builder(
            LocationFetchWorker.class,
            10, TimeUnit.MINUTES,
            5, TimeUnit.MINUTES)
            .build();

    // 提交任务,确保同一时刻只有一个任务实例在运行
    WorkManager.getInstance(this).enqueueUniquePeriodicWork(
            "LocationFetchTask",
            ExistingPeriodicWorkPolicy.REPLACE,
            locationTask);
}

方案二:AlarmManager + 单次位置请求(兼容旧版本)

如果你坚持使用AlarmManager,需要改造LocationCheckService,让它仅执行单次位置请求,完成后立即停止服务:

1. 改造LocationCheckService

public class LocationCheckService extends Service {
    private FusedLocationProviderClient fusedLocationClient;
    private LocationCallback locationCallback;

    @Override
    public void onCreate() {
        super.onCreate();
        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
        // 定义位置回调,拿到位置后立即停止监听并关闭服务
        locationCallback = new LocationCallback() {
            @Override
            public void onLocationResult(@NonNull LocationResult locationResult) {
                super.onLocationResult(locationResult);
                if (locationResult != null && locationResult.getLastLocation() != null) {
                    Location location = locationResult.getLastLocation();
                    // 处理位置信息
                    double lat = location.getLatitude();
                    double lng = location.getLongitude();
                    // TODO: 替换为你的实际业务代码

                    // 清理资源并停止服务
                    fusedLocationClient.removeLocationUpdates(locationCallback);
                    stopSelf();
                }
            }
        };
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        checkPermissionsAndFetchLocation();
        // 系统杀死服务后无需重启
        return START_NOT_STICKY;
    }

    private void checkPermissionsAndFetchLocation() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
                ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            stopSelf();
            return;
        }

        // 配置单次位置请求参数
        LocationRequest locationRequest = LocationRequest.create();
        locationRequest.setPriority(Priority.PRIORITY_BALANCED_POWER_ACCURACY);
        locationRequest.setNumUpdates(1); // 仅获取一次位置更新
        locationRequest.setInterval(0);
        locationRequest.setFastestInterval(0);

        fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper());
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // 确保移除位置监听,防止内存泄漏
        if (fusedLocationClient != null && locationCallback != null) {
            fusedLocationClient.removeLocationUpdates(locationCallback);
        }
    }
}

2. 调整AlarmManager调度逻辑

注意Android 8.0+版本需要使用前台Service,否则服务会被系统立即杀死:

private void setupLocationAlarm() {
    AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    Intent intent = new Intent(this, LocationCheckService.class);
    PendingIntent pendingIntent;

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        pendingIntent = PendingIntent.getForegroundService(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
    } else {
        pendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
    }

    long interval = 10 * 60 * 1000; // 10分钟对应的毫秒数
    long triggerTime = System.currentTimeMillis() + interval;

    // 适配不同Android版本的闹钟API
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);
    } else {
        alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, triggerTime, interval, pendingIntent);
    }
}

重要注意事项

  • 权限要求:必须申请ACCESS_FINE_LOCATION或ACCESS_COARSE_LOCATION权限,Android 10及以上版本还需要ACCESS_BACKGROUND_LOCATION才能在后台获取位置。
  • 电池优化:如果用户开启了电池优化,可能会影响任务执行,建议引导用户将应用加入电池优化白名单。
  • 前台通知:使用AlarmManager方案时,Android 8.0+版本需要让Service显示前台通知,否则会被系统立即终止。

内容的提问来源于stack exchange,提问作者Hitrene

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 08:45:23