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

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

一、项目介绍

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

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

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

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

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

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

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

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

调用 PowerManager 或 Runtime.exec() 发送关机命令

处理重复定时和 取消

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

二、相关技术与知识

AlarmManager

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

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

RTC Wake Alarm

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

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

关机/重启命令

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

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

TimePicker & SharedPreferences

TimePickerDialog 选时;

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

Foreground Service

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

设备兼容性

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

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

三、实现思路

UI 设计

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

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

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

数据存储

使用 SharedPreferences 存储 bootTime 和 shutdownTime(24h格式 “HH:mm”);

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

定时调度

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

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

执行开/关机

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

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

权限申请

运行时申请 RECEIVE_BOOT_COMPLETED、REQUEST_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 ===========================

android:orientation="vertical" android:padding="16dp"

android:layout_width="match_parent" android:layout_height="match_parent">

android:text="--" android:textSize="18sp"

android:layout_width="wrap_content"

android:layout_height="wrap_content"/>

相关发现

【操作系统篇】第四篇——线程(概念,实现方式,模型,状态与转换)
天国拯救通缉状态多久消失 偷窃通缉时间机制介绍
365速发国际welcome

天国拯救通缉状态多久消失 偷窃通缉时间机制介绍

🌼 07-20 🌻 6532
一键了解【腾讯看点】新玩法
365速发国际welcome

一键了解【腾讯看点】新玩法

🌼 07-10 🌻 2932
纵横百战
365速发国际welcome

纵横百战

🌼 07-23 🌻 4451