Android AlarmManager 闹钟服务

闹钟服务( AlarmManager) 会在在特定的时刻为我们广播一个指定的 Intent,简单说就是我们自己定一个时间,然后当到时间时,AlarmManager 会为我们广播一个我们设定好的 Intent

AlarmManager 主要是用来在某个时刻运行某些的代码的,即使 APP 在那个特定时间并没有运行

从 API 19 开始,Alarm 的机制都是非准确传递的,操作系统将会转换闹钟,来最小化唤醒和电池的使用,但某些新的 API 会支持严格准确的传递,比如

setWindow(int, long, long, PendingIntent)
setExact(int, long, PendingIntent)

targetSdkVersion 在 API 19 之前应用仍将继续使用以前的行为,所有的闹钟在要求准确传递的情况下都会准确传递

AlarmManager

Android 设备有自己的休眠策略,当长时间的无操作,设备会自动让 CPU 进入休眠状态

AlarmManager 则具有唤醒 CPU 的功能,可以保证每次需要执行特定任务时 CPU 都能正常工作, 或者说当 CPU 处于休眠时注册的闹钟会被保留(可以唤醒 CPU),但如果设备被关闭,或者重新 启动的话,闹钟将被清除

获得 AlarmManager 实例对象

AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);

AlarmManager 方法列表

方法 说明
set(int type,long startTime,PendingIntent pi) 一次性闹钟
setRepeating(int type,long startTime,long intervalTime,PendingIntent pi) 重复性闹钟,和 3 有区别,3 闹钟间隔时间不固定
setInexactRepeating(int type,long startTime,long intervalTime,PendingIntent pi 重复性闹钟,时间不固定
cancel(PendingIntent pi) 取消 AlarmManager 的定时服务
getNextAlarmClock() 得到下一个闹钟,返回值AlarmManager.AlarmClockInfo
setAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation) 和 set 方法类似,这个闹钟运行在系统处于低电模式时有效
setExact(int type, long triggerAtMillis, PendingIntent operation) 在规定的时间精确的执行闹钟,比set方法设置的精度更高
setTime(long millis) 设置系统墙上的时间
setTimeZone(String timeZone) 设置系统持续的默认时区
setWindow(int type, long windowStartMillis, long windowLengthMillis, PendingIntent operation) 设置一个闹钟在给定的时间窗触发。类似于set,该方法允许应用程序精确地控制操作系统调整闹钟触发时间的程度

关键参数解析

  1. type

    闹钟类型,有五个可选值

    说明
    AlarmManager.ELAPSED_REALTIME 闹钟在手机睡眠状态下不可用,该状态下闹钟使用相对时间(相对于系统启动开始),状态值为3;
    AlarmManager.ELAPSED_REALTIME_WAKEUP 闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟也使用相对时间,状态值为2;
    AlarmManager.RTC 闹钟在睡眠状态下不可用,该状态下闹钟使用绝对时间,即当前系统时间,状态值为1;
    AlarmManager.RTC_WAKEUP 表示闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟使用绝对时间,状态值为0;
    AlarmManager.POWER_OFF_WAKEUP 表示闹钟在手机关机状态下也能正常进行提示功能,所以是5个状态中用的最多的状态之一,该状态下闹钟也是用绝对时间,状态值为4;不过本状态好像受SDK版本影响,某些版本并不支持;
  2. startTime

    闹钟的第一次执行时间,以毫秒为单位,可以自定义时间,不过一般使用当前时间

    需要注意的是,该属性与 type 属性密切相关

    如果第一个参数对应的闹钟使用的是相对时间(ELAPSED_REALTIME 和 ELAPSED_REALTIME_WAKEUP),那么本属性就得使用相对时间(相对于系统启动时间来说),比如当前时间就表示为:SystemClock.elapsedRealtime();

    如果第一个参数对应的闹钟使用的是绝对时间 (RTC、RTC_WAKEUP、POWER_OFF_WAKEUP),那么本属性就得使用绝对时间,比如当前时间就表示 为:System.currentTimeMillis()

  3. intervalTime

    表示两次闹钟执行的间隔时间,也是以毫秒为单位

  4. PendingIntent

    绑定了闹钟的执行动作,比如发送一个广播、给出提示等等

    PendingIntent 是 Intent 的封装类

    需要注意的是,如果是通过启动服务来实现闹钟提示的话,PendingIntent 对象的获取就应该采用

    Pending.getService (Context c,int i,Intent intent,int j)
    

    如果是通过广播来实现闹钟提示的话,PendingIntent 对象的获取就应该采用

    PendingIntent.getBroadcast(Context c,int i,Intent intent,int j)
    

    如果是采用 Activity 的方式来实现闹钟提示的话,PendingIntent 对象的获取就应该采用

    PendingIntent.getActivity(Context c,int i,Intent intent,int j)
    

    如果这三种方法错用了的话,虽然不会报错,但是看不到闹钟提示效果

使用 AlarmManager 的一般流程

  1. 获得系统提供的 AlarmManager 服务的对象

    AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
    
  2. 创建 Intent 设置要启动的组件

    Intent intent = new Intent(MainActivity.this, ClockActivity.class);
    
  3. PendingIntent 对象设置动作,启动的是 Activity 还是 Service 又或者是广播

    PendingIntent pi = PendingIntent.getActivity(MainActivity.this, 0, intent, 0);
    
  4. 调用 alarmManager.set() 方法设置单次闹钟的闹钟类型,启动时间以及 PendingIntent 对象

    alarmManager.set(AlarmManager.RTC_WAKEUP,c.getTimeInMillis(), pi);
    
  5. 如果使用 alarmManager 发送广播,广播再激活 Activity 的话,则需要为 Intent 设置一个 flag

    i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    

一个简单的闹钟

使用 set() 方法可能有点不准,如果要更精确的话可以使用 setExtra() 方法来设置 AlarmManager

  1. 创建一个 空的 Android 项目 cn.twle.android.Alarm

  2. 在 res 目录上点右键,然后选择 Android resource directory,在弹出的界面中 Resource type 项选择 raw 然后点击 OK

  3. 下载音频文件 /static/i/android/alarm.mp3 并放到 res/raw 目录下

  4. 修改 activity_main.xml

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <Button
            android:id="@+id/btn_set"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="设置闹钟" />
    
        <Button
            android:id="@+id/btn_cancel"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="关闭闹钟"
            android:visibility="gone" />
    
    </LinearLayout>
    
  5. 创建闹铃布局 activity_clock.xml 作为闹钟响时 Activity 的布局

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" 
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
    </LinearLayout>
    
  6. 创建一个闹铃页面的 ClockActivity.java

    package cn.twle.android.alarm;
    
    import android.app.AlertDialog;
    import android.content.DialogInterface;
    import android.media.MediaPlayer;
    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    
    public class ClockActivity extends AppCompatActivity {
    
        private MediaPlayer mediaPlayer;
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_clock);
            mediaPlayer = mediaPlayer.create(this,R.raw.alarm);
            mediaPlayer.start();
            //创建一个闹钟提醒的对话框,点击确定关闭铃声与页面
            new AlertDialog.Builder(ClockActivity.this).setTitle("闹钟").setMessage("小猪小猪快起床~")
                    .setPositiveButton("关闭闹铃", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            mediaPlayer.stop();
                            ClockActivity.this.finish();
                        }
                    }).show();
        }
    }
    
  7. 创建一个广播接收器,接收闹铃服务 MsAlarmReceiver.java

    package cn.twle.android.alarm;
    
    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    
    public class MsAlarmReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Intent i = new Intent(context,ClockActivity.class);
            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(i);
        }
    }
    
  8. 修改 AndroidManifest.xml 添加 ActivityMsAlarmReceiver

    <activity android:name=".ClockActivity"/>
    <receiver android:name=".MsAlarmReceiver"/>
    
  9. 修改 MainActivity.java

    package cn.twle.android.alarm;
    
    import android.app.AlarmManager;
    import android.app.PendingIntent;
    import android.app.TimePickerDialog;
    import android.content.Intent;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.os.Build;
    import android.view.View;
    import android.widget.Button;
    import android.widget.TimePicker;
    import android.widget.Toast;
    
    import java.util.Calendar;
    
    public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    
        private Button btn_set;
        private Button btn_cancel;
        private AlarmManager alarmManager;
        private PendingIntent pi;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            bindViews();
        }
    
        private void bindViews() {
            btn_set = (Button) findViewById(R.id.btn_set);
            btn_cancel = (Button) findViewById(R.id.btn_cancel);
            alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
    
            Intent intent = new Intent(MainActivity.this, ClockActivity.class);
            pi = PendingIntent.getActivity(MainActivity.this, 0, intent, 0);
    
            btn_set.setOnClickListener(this);
            btn_cancel.setOnClickListener(this);
    
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()){
                case R.id.btn_set:
                    Calendar currentTime = Calendar.getInstance();
                    new TimePickerDialog(MainActivity.this, 0,
                            new TimePickerDialog.OnTimeSetListener() {
                                @Override
                                public void onTimeSet(TimePicker view,
                                                      int hourOfDay, int minute) {
                                    //设置当前时间
                                    Calendar c = Calendar.getInstance();
                                    c.setTimeInMillis(System.currentTimeMillis());
                                    // 根据用户选择的时间来设置Calendar对象
                                    c.set(Calendar.HOUR, hourOfDay);
                                    c.set(Calendar.MINUTE, minute);
                                    // ②设置AlarmManager在Calendar对应的时间启动Activity
    
                                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    
                                        alarmManager.setExact(AlarmManager.RTC_WAKEUP, c.getTimeInMillis(), pi);
                                    }else {
                                        alarmManager.set(AlarmManager.RTC_WAKEUP, c.getTimeInMillis(), pi);
                                    }
    
                                    // 提示闹钟设置完毕:
                                    Toast.makeText(MainActivity.this, "闹钟设置完毕",
                                            Toast.LENGTH_SHORT).show();
                                }
                            }, currentTime.get(Calendar.HOUR_OF_DAY), currentTime
                            .get(Calendar.MINUTE), false).show();
                    btn_cancel.setVisibility(View.VISIBLE);
                    break;
                case R.id.btn_cancel:
                    alarmManager.cancel(pi);
                    btn_cancel.setVisibility(View.GONE);
                    Toast.makeText(MainActivity.this, "闹钟已取消", Toast.LENGTH_SHORT)
                            .show();
                    break;
            }
        }
    }
    

不能用啊,算了,现在没空去想要怎么优化

参考文档

  1. Android 官方 API 文档: AlarmManager

Android 基础教程

关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.