Android实现系统定时开关机(附带源码)

一、项目介绍

在某些嵌入式 Android、物联网盒子或特殊工业场景中,需要在预设时间自动开机关机,以便节能或定时执行任务。标准 Android 手机并不支持任意时刻自动开机,但部分设备厂商或定制固件提供了相关接口。通常思路包括:

  1. 开机时间写入底层 RTC:在关机前通过 AlarmManager 或直接写入 /sys/class/rtc/rtc0/wakealarm 实现;

  2. 关机定时:使用 PowerManager.reboot() 或发送关机广播;

  3. 用户界面:提供设置界面,选择开、关机时刻,并在后台调度任务;

  4. 权限与系统签名:关机、重启等操作通常需要系统权限或签名,普通应用需借助 ROOT 或系统应用。

本教程假设设备已授予系统签名权限或 ROOT 环境,可执行开关机命令;并演示如何:

  • 在应用内 设置开/关机时间

  • 使用 AlarmManager 安排关机或写入 Wake Alarm

  • 调用 PowerManagerRuntime.exec() 发送关机命令

  • 处理重复定时取消

  • UI 采用 TimePicker 选择时间,并保存到 SharedPreferences


二、相关技术与知识

  1. AlarmManager

    • 用于安排在未来某个时间发送广播或启动 Service

    • setExactAndAllowWhileIdle() 保证在 Doze 模式下精准触发。

  2. RTC Wake Alarm

    • 通过向 /sys/class/rtc/rtc0/wakealarm 写入时间戳,设置下次开机唤醒。

    • Android 系统原生支持 AlarmManager.setRtcWakeup(),但底层实现可兼容写文件。

  3. 关机/重启命令

    • 普通应用:Intent 调用 ACTION_REQUEST_SHUTDOWN(需权限),可唤起关机界面;

    • 系统应用/ROOT:通过 PowerManager.reboot(null)Runtime.getRuntime().exec("reboot -p") 强制关机;

  4. TimePicker & SharedPreferences

    • TimePickerDialog 选时;

    • 将选择结果保存到 SharedPreferences,以便重启后读取计划。

  5. Foreground Service

    • 如果需要在后台长期运行,可使用前台服务维持进程不被杀死;提供通知栏入口。

  6. 设备兼容性

    • 不同 Android 版本对关机命令权限控制不同,需根据设备定制;

    • 部分设备在未获得系统签名时,无法写 /sys 或调用 PowerManager.reboot()


三、实现思路

  1. UI 设计

    • 两个按钮:设置“定时开机”和“定时关机”;

    • 分别弹出 TimePickerDialog 供用户选择时分;

    • 列表展示当前已设置的任务,并可取消。

  2. 数据存储

    • 使用 SharedPreferences 存储 bootTimeshutdownTime(24h格式 “HH:mm”);

    • 可扩展为多条计划,使用 Room 存储。

  3. 定时调度

    • 关机:在用户设定的 shutdownTime 到来时,AlarmManager 发送广播到 ShutdownReceiver,在 onReceive 中调用关机逻辑;

    • 开机:在关机前写入 RTC Wake Alarm,或在系统启动后读取 bootTime 并再次设定下一次关机等。

  4. 执行开/关机

    • 关机:如果是系统应用,可直接 PowerManager pm = getSystemService(PowerManager.class); pm.reboot(null);exec("reboot -p")

    • 开机:由底层通过 RTC 唤醒,不在应用层执行;应用可在 BOOT_COMPLETED 广播中监听,并在开机时执行初始化任务。

  5. 权限申请

    • 运行时申请 RECEIVE_BOOT_COMPLETEDREQUEST_SHUTDOWN 等;

    • 关机权限 android.permission.SHUTDOWN 通常为系统权限。


四、完整代码

// ==============================================
// 文件:MainActivity.java
// 功能:定时开关机设置、调度与执行示例
// 包含:布局 XML、Manifest、Receiver、一处整合
// ==============================================

package com.example.timerpower;

import android.app.*;
import android.content.*;
import android.os.*;
import android.provider.Settings;
import android.widget.*;
import androidx.annotation.*;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
    private Button btnSetBoot, btnSetShutdown;
    private TextView tvInfo;
    private SharedPreferences prefs;

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

        btnSetBoot     = findViewById(R.id.btnSetBoot);
        btnSetShutdown = findViewById(R.id.btnSetShutdown);
        tvInfo         = findViewById(R.id.tvInfo);

        prefs = getSharedPreferences("timer_power", MODE_PRIVATE);
        updateInfo();

        btnSetBoot.setOnClickListener(v -> showTimePicker(true));
        btnSetShutdown.setOnClickListener(v -> showTimePicker(false));
    }

    private void showTimePicker(boolean isBoot) {
        Calendar c = Calendar.getInstance();
        new TimePickerDialog(this,
            (view, h, m) -> {
                String key = isBoot ? "bootTime" : "shutdownTime";
                prefs.edit().putString(key,
                    String.format("%02d:%02d", h, m)).apply();
                schedule(isBoot, h, m);
                updateInfo();
            },
            c.get(Calendar.HOUR_OF_DAY),
            c.get(Calendar.MINUTE), true).show();
    }

    private void updateInfo() {
        String boot = prefs.getString("bootTime", "--:--");
        String off  = prefs.getString("shutdownTime", "--:--");
        tvInfo.setText("开机: " + boot + "\n关机: " + off);
    }

    private void schedule(boolean isBoot, int h, int m) {
        AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
        Intent intent = new Intent(this,
          isBoot ? BootReceiver.class : ShutdownReceiver.class);
        PendingIntent pi = PendingIntent.getBroadcast(this,
            isBoot? 0:1, intent, 0);
        Calendar c = Calendar.getInstance();
        c.set(Calendar.HOUR_OF_DAY, h);
        c.set(Calendar.MINUTE, m);
        c.set(Calendar.SECOND, 0);
        long trigger = c.getTimeInMillis();
        if (trigger < System.currentTimeMillis())
            trigger += 24*3600*1000; // 次日
        am.setExactAndAllowWhileIdle(
            AlarmManager.RTC_WAKEUP, trigger, pi);
    }
}

// ------- 关机 Receiver -------
public class ShutdownReceiver extends BroadcastReceiver {
    @Override public void onReceive(Context ctx, Intent i) {
        try {
            // 系统关机
            Process p = Runtime.getRuntime()
                .exec(new String[]{"su","-c","reboot -p"});
            p.waitFor();
        } catch (Exception e) { e.printStackTrace(); }
    }
}

// ------- 开机 Receiver -------
public class BootReceiver extends BroadcastReceiver {
    @Override public void onReceive(Context ctx, Intent i) {
        // 在系统启动后可执行初始化逻辑
        Toast.makeText(ctx,
          "定时开机已到", Toast.LENGTH_LONG).show();
    }
}

/*
=========================== res/layout/activity_main.xml ===========================

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:padding="16dp"
    android:layout_width="match_parent" android:layout_height="match_parent">

    <TextView android:id="@+id/tvInfo"
        android:text="--" android:textSize="18sp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <Button android:id="@+id/btnSetBoot"
        android:layout_marginTop="24dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="设置定时开机"/>

    <Button android:id="@+id/btnSetShutdown"
        android:layout_marginTop="16dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="设置定时关机"/>
</LinearLayout>
*/

/*
=========================== AndroidManifest.xml ===========================

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.timerpower">

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <!-- 如果需要 su 权限 -->
    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>

    <application ...>
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <receiver android:name=".ShutdownReceiver"/>
        <receiver android:name=".BootReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
            </intent-filter>
        </receiver>
    </application>
</manifest>
*/

五、方法解读

  • AlarmManager & RTC_WAKEUP:利用系统闹钟在指定时间触发广播,结合 su reboot -p 强制关机。

  • BootCompleted:设备每次开机后,应用无需启动即可接收广播,执行后续逻辑。

  • TimePickerDialog:在 UI 层获取用户设定的时分,并保存到 SharedPreferences

  • 权限与兼容:执行关机命令需 ROOT 或系统签名权限;普通应用仅可唤起关机界面。


六、项目总结

  • 适用场景:定制化设备、物联网、智能盒子等需定时开关机的环境;

  • 限制:绝大多数普通 Android 手机不支持应用层定时开机,需底层厂商支持;

  • 扩展:可增加“重复”属性(每天/每周)、多条计划列表、取消计划功能;

  • 安全:Root/系统权限操作需谨慎,避免恶意使用;

提示:请根据目标设备特性(是否支持 su、定制固件)调整方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值