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

如何用AlarmManager定时启停FusedLocationProviderClient的位置更新

解决方案:用AlarmManager定时启停后台位置更新

你的思路完全正确——AlarmManager就是解决这种定时任务的最佳方案,既能精准控制启停时间,又能避免夜间不必要的电量消耗。下面给你一套完整的实现步骤和代码示例:

一、封装位置更新的启停方法

首先把开启和关闭位置更新的逻辑封装成可复用方法,方便后续调用:

// 可放在MainActivity或单独的LocationHelper工具类中
private fun startLocationUpdates() {
    val locationRequest = LocationRequest.create().apply {
        interval = 10000 // 根据业务需求调整更新间隔
        fastestInterval = 5000
        priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY
    }

    val pendingIntent = PendingIntent.getBroadcast(
        this,
        0,
        Intent(this, YourLocationReceiver::class.java),
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
    )

    fusedLocationProviderClient.requestLocationUpdates(
        locationRequest,
        pendingIntent
    )
}

private fun stopLocationUpdates() {
    val pendingIntent = PendingIntent.getBroadcast(
        this,
        0,
        Intent(this, YourLocationReceiver::class.java),
        PendingIntent.FLAG_NO_CREATE or PendingIntent.FLAG_IMMUTABLE
    )
    pendingIntent?.let {
        fusedLocationProviderClient.removeLocationUpdates(it)
        it.cancel()
    }
}

二、创建定时任务的广播接收器

新建一个LocationSchedulerReceiver,专门处理AlarmManager触发的启停指令:

class LocationSchedulerReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        context ?: return
        val fusedClient = LocationServices.getFusedLocationProviderClient(context)
        
        when (intent?.action) {
            ACTION_STOP_LOCATION -> {
                val pendingIntent = PendingIntent.getBroadcast(
                    context,
                    0,
                    Intent(context, YourLocationReceiver::class.java),
                    PendingIntent.FLAG_NO_CREATE or PendingIntent.FLAG_IMMUTABLE
                )
                pendingIntent?.let {
                    fusedClient.removeLocationUpdates(it)
                    it.cancel()
                }
            }
            ACTION_START_LOCATION -> {
                val locationRequest = LocationRequest.create().apply {
                    interval = 10000
                    fastestInterval = 5000
                    priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY
                }
                val pendingIntent = PendingIntent.getBroadcast(
                    context,
                    0,
                    Intent(context, YourLocationReceiver::class.java),
                    PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
                )
                fusedClient.requestLocationUpdates(locationRequest, pendingIntent)
            }
        }
    }

    companion object {
        const val ACTION_STOP_LOCATION = "com.yourpackage.action.STOP_LOCATION"
        const val ACTION_START_LOCATION = "com.yourpackage.action.START_LOCATION"
    }
}

别忘了在AndroidManifest.xml中注册这个接收器:

<receiver
    android:name=".LocationSchedulerReceiver"
    android:exported="false">
    <intent-filter>
        <action android:name="com.yourpackage.action.STOP_LOCATION" />
        <action android:name="com.yourpackage.action.START_LOCATION" />
    </intent-filter>
</receiver>

三、设置每日重复的Alarm任务

创建方法配置AlarmManager,分别设置22:00的停止任务和6:00的启动任务:

private fun setupDailyLocationAlarms() {
    val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager

    // 配置22:00停止更新的PendingIntent
    val stopIntent = Intent(this, LocationSchedulerReceiver::class.java).apply {
        action = LocationSchedulerReceiver.ACTION_STOP_LOCATION
    }
    val stopPendingIntent = PendingIntent.getBroadcast(
        this,
        1, // 用不同requestCode区分两个任务
        stopIntent,
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
    )

    // 配置6:00开启更新的PendingIntent
    val startIntent = Intent(this, LocationSchedulerReceiver::class.java).apply {
        action = LocationSchedulerReceiver.ACTION_START_LOCATION
    }
    val startPendingIntent = PendingIntent.getBroadcast(
        this,
        2,
        startIntent,
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
    )

    // 设置停止任务:每日22:00触发
    val stopCalendar = Calendar.getInstance().apply {
        set(Calendar.HOUR_OF_DAY, 22)
        set(Calendar.MINUTE, 0)
        set(Calendar.SECOND, 0)
        // 如果当前时间已过22:00,自动顺延至次日
        if (timeInMillis < System.currentTimeMillis()) {
            add(Calendar.DAY_OF_YEAR, 1)
        }
    }

    // 设置启动任务:每日6:00触发
    val startCalendar = Calendar.getInstance().apply {
        set(Calendar.HOUR_OF_DAY, 6)
        set(Calendar.MINUTE, 0)
        set(Calendar.SECOND, 0)
        if (timeInMillis < System.currentTimeMillis()) {
            add(Calendar.DAY_OF_YEAR, 1)
        }
    }

    // 适配Android版本:Android 12+需要SCHEDULE_EXACT_ALARM权限
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        if (alarmManager.canScheduleExactAlarms()) {
            alarmManager.setRepeating(
                AlarmManager.RTC_WAKEUP,
                stopCalendar.timeInMillis,
                AlarmManager.INTERVAL_DAY,
                stopPendingIntent
            )
            alarmManager.setRepeating(
                AlarmManager.RTC_WAKEUP,
                startCalendar.timeInMillis,
                AlarmManager.INTERVAL_DAY,
                startPendingIntent
            )
        } else {
            // 引导用户开启精确闹钟权限
            startActivity(Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM))
        }
    } else {
        alarmManager.setRepeating(
            AlarmManager.RTC_WAKEUP,
            stopCalendar.timeInMillis,
            AlarmManager.INTERVAL_DAY,
            stopPendingIntent
        )
        alarmManager.setRepeating(
            AlarmManager.RTC_WAKEUP,
            startCalendar.timeInMillis,
            AlarmManager.INTERVAL_DAY,
            startPendingIntent
        )
    }
}

四、首次启动与权限处理

在MainActivity的onCreate中完成首次位置更新开启和闹钟设置,避免重复配置:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)

    // 用SharedPreferences标记是否已设置过闹钟,防止重复创建
    val sharedPref = getSharedPreferences("LocationPrefs", MODE_PRIVATE)
    val alarmsSet = sharedPref.getBoolean("alarms_set", false)

    if (!alarmsSet) {
        // 首次启动立即开启位置更新
        startLocationUpdates()
        // 设置每日定时任务
        setupDailyLocationAlarms()
        // 标记已完成配置
        sharedPref.edit().putBoolean("alarms_set", true).apply()
    }
}

五、处理设备重启(可选但重要)

设备重启后AlarmManager任务会被清除,需添加开机广播接收器重新配置:

  1. 新建BootCompletedReceiver:
class BootCompletedReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        if (intent?.action == Intent.ACTION_BOOT_COMPLETED) {
            context?.let { ctx ->
                // 重新设置每日闹钟
                val alarmManager = ctx.getSystemService(Context.ALARM_SERVICE) as AlarmManager
                // 检查当前时段,若在6:00-22:00之间则立即开启位置更新
                val currentHour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY)
                if (currentHour in 6..21) {
                    val fusedClient = LocationServices.getFusedLocationProviderClient(ctx)
                    val locationRequest = LocationRequest.create().apply {
                        interval = 10000
                        fastestInterval = 5000
                        priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY
                    }
                    val pendingIntent = PendingIntent.getBroadcast(
                        ctx,
                        0,
                        Intent(ctx, YourLocationReceiver::class.java),
                        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
                    )
                    fusedClient.requestLocationUpdates(locationRequest, pendingIntent)
                }
                // 复用之前的闹钟配置方法(建议将setupDailyLocationAlarms改为静态工具方法)
                setupDailyLocationAlarms()
            }
        }
    }
}
  1. 在Manifest中注册并添加权限:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" /> <!-- Android 12+ -->

<receiver
    android:name=".BootCompletedReceiver"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

关键注意事项

  • 权限检查:确保已申请ACCESS_FINE_LOCATION/ACCESS_COARSE_LOCATION以及ACCESS_BACKGROUND_LOCATION权限,否则后台位置更新无法正常工作。
  • Android版本适配:Android 12及以上必须申请SCHEDULE_EXACT_ALARM权限,且需先检查alarmManager.canScheduleExactAlarms()返回值,若为false则引导用户到设置页开启。
  • PendingIntent标记:务必使用FLAG_IMMUTABLE(Android 12+强制要求),避免安全风险。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:22:23