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,该方法允许应用程序精确地控制操作系统调整闹钟触发时间的程度 |
关键参数解析
-
type
闹钟类型,有五个可选值
值 说明 AlarmManager.ELAPSED_REALTIME 闹钟在手机睡眠状态下不可用,该状态下闹钟使用相对时间(相对于系统启动开始),状态值为3; AlarmManager.ELAPSED_REALTIME_WAKEUP 闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟也使用相对时间,状态值为2; AlarmManager.RTC 闹钟在睡眠状态下不可用,该状态下闹钟使用绝对时间,即当前系统时间,状态值为1; AlarmManager.RTC_WAKEUP 表示闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟使用绝对时间,状态值为0; AlarmManager.POWER_OFF_WAKEUP 表示闹钟在手机关机状态下也能正常进行提示功能,所以是5个状态中用的最多的状态之一,该状态下闹钟也是用绝对时间,状态值为4;不过本状态好像受SDK版本影响,某些版本并不支持; -
startTime
闹钟的第一次执行时间,以毫秒为单位,可以自定义时间,不过一般使用当前时间
需要注意的是,该属性与 type 属性密切相关
如果第一个参数对应的闹钟使用的是相对时间(ELAPSED_REALTIME 和 ELAPSED_REALTIME_WAKEUP),那么本属性就得使用相对时间(相对于系统启动时间来说),比如当前时间就表示为:SystemClock.elapsedRealtime();
如果第一个参数对应的闹钟使用的是绝对时间 (RTC、RTC_WAKEUP、POWER_OFF_WAKEUP),那么本属性就得使用绝对时间,比如当前时间就表示 为:System.currentTimeMillis()
-
intervalTime
表示两次闹钟执行的间隔时间,也是以毫秒为单位
-
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 的一般流程
-
获得系统提供的 AlarmManager 服务的对象
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
-
创建 Intent 设置要启动的组件
Intent intent = new Intent(MainActivity.this, ClockActivity.class);
-
PendingIntent 对象设置动作,启动的是 Activity 还是 Service 又或者是广播
PendingIntent pi = PendingIntent.getActivity(MainActivity.this, 0, intent, 0);
-
调用
alarmManager.set()
方法设置单次闹钟的闹钟类型,启动时间以及 PendingIntent 对象alarmManager.set(AlarmManager.RTC_WAKEUP,c.getTimeInMillis(), pi);
-
如果使用 alarmManager 发送广播,广播再激活 Activity 的话,则需要为 Intent 设置一个 flag
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
一个简单的闹钟
使用 set()
方法可能有点不准,如果要更精确的话可以使用 setExtra()
方法来设置 AlarmManager
-
创建一个 空的 Android 项目
cn.twle.android.Alarm
-
在 res 目录上点右键,然后选择
Android resource directory
,在弹出的界面中Resource type
项选择raw
然后点击 OK -
下载音频文件 /static/i/android/alarm.mp3 并放到
res/raw
目录下 -
修改
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>
-
创建闹铃布局
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>
-
创建一个闹铃页面的 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(); } }
-
创建一个广播接收器,接收闹铃服务
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); } }
-
修改
AndroidManifest.xml
添加Activity
和MsAlarmReceiver
<activity android:name=".ClockActivity"/> <receiver android:name=".MsAlarmReceiver"/>
-
修改 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; } } }
不能用啊,算了,现在没空去想要怎么优化