1、新建一个activity,命名为AlarmActivity,如下:

public class AlarmActivity extends AppCompatActivity {
    private TextView tv_alarm;
    private int mDelay;

    private int[] delayArr = {5, 10, 15, 20, 25, 30};
    private String[] delayDescArr = {"5秒", "10秒", "15秒", "20秒", "25秒", "30秒"};

    private String ALARM_EVENT = "com.example.senior.activity.broad.AlarmActivity.AlarmReceiver";
    private static String mDesc = ""; // 闹钟时间到达的描述
    private static boolean isArrived = false; // 闹钟时间是否到达

    private AlarmReceiver alarmReceiver; // 闹钟的广播接收器

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_alarm);
        tv_alarm = findViewById(R.id.tv_alarm);
        initButton();
        initDelaySpinner();
    }

    @Override
    protected void onStart() {
        super.onStart();
        // 从Android9.0开始,系统不在支持静态广播,应用广播只能通过动态注册
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            alarmReceiver = new AlarmReceiver(); // 创建一个闹钟的广播接收器
            IntentFilter filter = new IntentFilter(ALARM_EVENT);
            registerReceiver(alarmReceiver, filter); // 注册广播接收器,注册之后才能正常接收广播
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            unregisterReceiver(alarmReceiver);
        }
    }

    private void initButton() {
        findViewById(R.id.btn_alarm).setOnClickListener(v -> {
            // 创建一个广播事件的意图
            Intent intent = new Intent(ALARM_EVENT);
            // 创建一个用于广播的延时意图
            PendingIntent pIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);;
            // 从系统服务中获取闹钟管理器
            AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
            Calendar calendar = Calendar.getInstance();
            calendar.setTimeInMillis(System.currentTimeMillis());
            calendar.add(Calendar.SECOND, mDelay); // 给当前时间加上若干秒
            // 开始设定闹钟,延时若干秒后,携带延时意图发送闹钟广播
            alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pIntent);
            mDesc = DateUtil.getNowTime() + " 设置闹钟";
            tv_alarm.setText(mDesc);
        });
    }

    private void initDelaySpinner() {
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.item_select, delayDescArr);
        Spinner sp_delay = findViewById(R.id.sp_delay);
        sp_delay.setPrompt("请选择闹钟延时");
        sp_delay.setAdapter(adapter);
        sp_delay.setSelection(0);
        sp_delay.setOnItemSelectedListener(new DelaySelectedListener());
    }

    private class DelaySelectedListener implements AdapterView.OnItemSelectedListener {

        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            mDelay = delayArr[position];
        }

        @Override
        public void onNothingSelected(AdapterView<?> parent) {

        }
    }

    public class AlarmReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent == null) {
                return;
            }
//            if (tv_alarm != null && !isArrived) {
//                isArrived = true;
                mDesc = String.format("%s\n%s 闹钟时间到达", mDesc, DateUtil.getNowTime());
                tv_alarm.setText(mDesc);
//            }
        }
    }
}

2、AlarmActivity对应的布局文件为activity_alarm,如下:

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

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="40dp">

        <TextView
            android:id="@+id/tv_delay"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="闹钟延时"
            android:textColor="@color/black"
            android:textSize="17sp" />

        <Spinner
            android:id="@+id/sp_delay"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:spinnerMode="dialog"
            android:layout_toRightOf="@id/tv_delay" />

    </RelativeLayout>

    <Button
        android:id="@+id/btn_alarm"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="设置闹钟"
        android:textSize="20sp" />

    <TextView
        android:id="@+id/tv_alarm"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="这里是闹钟的消息"
        android:textColor="@color/contentGray"
        android:textSize="15sp" />

</LinearLayout>

3、日期帮助类为DateUtil,如下:

public class DateUtil {
    public static String getNowDateTime(String formatStr) {
        if (TextUtils.isEmpty(formatStr)) {
            formatStr = "yyyyMMddHHmmss";
        }
        SimpleDateFormat sdf = new SimpleDateFormat(formatStr);
        return sdf.format(new Date());
    }

    public static String getNowDateTime() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return sdf.format(new Date());
    }

    public static String getNowTime() {
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        return sdf.format(new Date());
    }

    public static String getNowTimeDetail() {
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
        return sdf.format(new Date());
    }

    public static String formatDate(long time) {
        Date date = new Date(time);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss");
        return sdf.format(date);
    }
}

4、Android9.0之前的静态广播如下,再AndroidManifest.xml中设置,如下:

注意:要在 这个节点中写 receiver 标签

<receiver android:name=".activity.broad.AlarmActivity$AlarmReceiver"
            android:exported="true">
            <intent-filter>
                <action
                    android:name="com.example.senior.activity.broad.AlarmActivity.AlarmReceiver" />
            </intent-filter>
        </receiver>

5、效果图如下:

6、说明

  • PendingIntent 是延迟的意图,只要不是立即传递的消息,都用PendingIntent
    • 而平常开发通过activity和broadcast传递消息都要求立即处理,这里用Intent
    • PendingIntent 调用了getBroadcast 方法,表示这次携带的消息用于发送广播
  • AlarmManager 的set 方法用于设置一次性定时器
    • 该方法的第一个参数表示定时器类型,AlarmManager.RTC_WAKEUP 指:定时器即使在睡眠状态下也会启用
    • 第二个参数表示任务的执行时间
    • 第三个参数表示携带消息的延时任务