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

Android应用停止/销毁后保持CountDownTimer运行并实现锁屏

解决Android应用销毁后仍能倒计时并触发锁屏的问题

嘿,作为Android和Java新手碰到这个需求太正常了!你想让CountDownTimer在应用退出甚至销毁后还能跑,还能触发锁屏——但先给你说个关键点:CountDownTimer本身是绑定在Activity生命周期上的,它依赖主线程的Handler,Activity一销毁它就跟着停了,光靠onStop()根本搞不定,得用后台定时组件+设备管理员权限来实现。下面给你一步步拆解:

第一步:先搞定锁屏的必要权限(DevicePolicyManager)

要让应用能锁屏,必须先获取设备管理员权限,这是Android系统的硬性要求,步骤如下:

1. 创建DeviceAdminReceiver子类

新建一个MyDeviceAdminReceiver.java,用来接收设备管理员相关的系统回调:

public class MyDeviceAdminReceiver extends DeviceAdminReceiver {
    // 这里可以留空,或者按需实现onEnabled、onDisabled等回调
}

2. 在Manifest里注册Receiver

打开AndroidManifest.xml,添加这个Receiver的注册,还要声明权限和配置文件:

<receiver
    android:name=".MyDeviceAdminReceiver"
    android:permission="android.permission.BIND_DEVICE_ADMIN">
    <meta-data
        android:name="android.app.device_admin"
        android:resource="@xml/device_admin" />
    <intent-filter>
        <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
    </intent-filter>
</receiver>

3. 创建设备管理员配置文件

res/xml目录下新建device_admin.xml(如果没有xml目录就自己建),声明我们需要的锁屏权限:

<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-policies>
        <force-lock /> <!-- 这个就是锁屏需要的权限 -->
    </uses-policies>
</device-admin>

4. 请求用户授予设备管理员权限

在你的SetTimeActivity里,添加请求权限的方法,比如在onCreate()或者点击设置按钮时调用:

private void requestDeviceAdminPermission() {
    Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
    intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, 
            new ComponentName(this, MyDeviceAdminReceiver.class));
    intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, 
            "需要获取锁屏权限,才能在倒计时结束后自动锁屏");
    startActivityForResult(intent, 1001); // 1001是请求码,随便设一个就行
}

第二步:用后台定时任务替代CountDownTimer

因为CountDownTimer没法脱离Activity存活,我们得用Android的后台定时组件,这里推荐两种方案:

方案一:WorkManager(推荐,适配Android 8+后台限制)

WorkManager是Google推荐的后台任务调度工具,自动适配不同Android版本的后台限制,代码更简单:

1. 添加WorkManager依赖

打开build.gradle(Module level),在dependencies里添加:

implementation "androidx.work:work-runtime:2.8.1" // 可以用最新版本

2. 创建Worker类执行锁屏操作

新建LockScreenWorker.java,这是后台任务的执行逻辑:

public class LockScreenWorker extends Worker {
    private DevicePolicyManager devicePolicyManager;

    public LockScreenWorker(@NonNull Context context, @NonNull WorkerParameters params) {
        super(context, params);
        devicePolicyManager = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
    }

    @NonNull
    @Override
    public Result doWork() {
        // 检查是否已经获取设备管理员权限
        ComponentName adminComponent = new ComponentName(getApplicationContext(), MyDeviceAdminReceiver.class);
        if (devicePolicyManager.isAdminActive(adminComponent)) {
            devicePolicyManager.lockNow(); // 执行锁屏
            return Result.success(); // 任务成功完成
        } else {
            // 没有权限,返回失败
            return Result.failure();
        }
    }
}

3. 调度定时任务

在你的SetTimeActivity里,当用户设置好倒计时分钟数后,用WorkManager调度任务:

// 假设用户输入的分钟数存在min变量里,转换成毫秒
long delayMillis = min * 60 * 1000L;

// 创建一次性任务
OneTimeWorkRequest lockScreenTask = new OneTimeWorkRequest.Builder(LockScreenWorker.class)
        .setInitialDelay(delayMillis, TimeUnit.MILLISECONDS)
        .build();

// 提交任务给WorkManager
WorkManager.getInstance(this).enqueue(lockScreenTask);

这样不管应用是退后台还是被销毁,到时间WorkManager都会自动触发这个任务,执行锁屏。

方案二:AlarmManager(适合需要精确计时的场景)

如果你的需求是非常精确的倒计时,可以用AlarmManager,但Android 8+之后对后台闹钟有严格限制,需要额外处理:

1. 创建BroadcastReceiver接收闹钟触发

新建LockScreenReceiver.java

public class LockScreenReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        DevicePolicyManager devicePolicyManager = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
        ComponentName adminComponent = new ComponentName(context, MyDeviceAdminReceiver.class);
        if (devicePolicyManager.isAdminActive(adminComponent)) {
            devicePolicyManager.lockNow();
        }
    }
}

在Manifest里注册这个Receiver:

<receiver android:name=".LockScreenReceiver" />

2. 调度闹钟任务

SetTimeActivity里添加调度代码:

// 计算触发时间:当前时间 + 倒计时毫秒数
long triggerTime = System.currentTimeMillis() + min * 60 * 1000L;

Intent intent = new Intent(this, LockScreenReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
        this, 
        0, 
        intent, 
        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);

AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    // Android 12+需要检查精确闹钟权限
    if (alarmManager.canScheduleExactAlarms()) {
        alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);
    } else {
        // 跳转到权限设置页面让用户开启
        Intent permissionIntent = new Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM);
        startActivity(permissionIntent);
    }
} else {
    // 低版本直接设置
    alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);
}

最后提醒几个关键点

  • 设备管理员权限是必须的,没有这个权限,任何代码都没法触发锁屏;
  • 如果用户手动清除了应用的后台任务(比如在最近应用列表里划掉),WorkManager/AlarmManager的任务可能会被系统取消,这是Android的后台限制,没法完全避免,但可以引导用户把应用加入系统白名单;
  • 如果你之前用了CountDownTimer来显示倒计时UI,那可以保留它,但后台计时必须交给WorkManager/AlarmManager,UI上的倒计时只是给用户看的,真正的触发逻辑要靠后台组件。

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

火山引擎 最新活动