AlarmManager
Timer有一个明显的短板,它并不太适用于那些需要长期在后台运行的定时任务。我们都知道,为了能让电池更加耐用,每种手机都会有自己的休眠策略,Android 手机就会在长时间不操作的情况下自动让 CPU 进入到睡眠状态,这就有可能导致 Timer 中的定时任务无法正常运行。另一方面,其实也可以通过handler的poseDelay方法来实现定时操作,也是不靠谱的,因为默认handler依赖于线程(main线程或者子线程),所以只要进程被杀死,所有相关的线程都被杀死,所以handler中的定时操作就无效了。timer也一样,因为timer实际上是另启一个子线程,进程被杀了,子线程当然也被杀了,所以time失效。。。。而 Alarm 机制则不存在这种情况,它通过pendingintent具有唤醒 未启动的进程 的功能,即可以保证每次需要执行定时任务的时候 CPU 都能正常工作。但是闹钟当设备关机和重启后,闹钟将会被清除。一张图总结一下区别
ELAPSED_REALTIME 表示让定时任务的触发时间从系统开机开始算起,但不会唤醒 CPU
AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
long triggerAtTime = SystemClock.elapsedRealtime() + 10 * 1000;
//10s执行一次pendingIntent
manager.set(AlarmManager.ELAPSED_REALTIME, triggerAtTime, pendingIntent);
RTC 表示让定时任务的触发时间从 1970 年 1月 1 日 0 点开始算起,但不会唤醒 CPU
long triggerAtTime = System.currentTimeMillis() + 10 * 1000;
//10s执行一次pendingIntent
manager.set(AlarmManager.RTC, triggerAtTime, pendingIntent);
SystemClock.elapsedRealtime()方法可以获取到系统开机至今所经历时间的毫秒数
System.currentTimeMillis()方法可以获取到1970年1月1日0点至今所经历时间的毫秒数
long repeat = 10000;
//从当前时间开始,10s中执行一次PendingIntent
mAlarmManager.setRepeating(AlarmManager.RTC, System.currentTimeMillis(), repeat, pendingIntent);
PendingIntent
Intent 更加倾向于去立即执行某个动作,而 PendingIntent 更加倾向于在某个合适的时机去执行某个动作。所以,也可以把 PendingIntent 简单地理解为延迟执行的 Intent。getActivity()方法、getBroadcast()方法、getService()方法
Notification
sdk16以后可以这样使用,点击该通知后启动NextActivity Intent acIntent = new Intent(this, NextActivity.class);
NotificationManager nm = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
PendingIntent pi = PendingIntent.getActivity(this, 0, acIntent, 0);
Notification notify = new Notification.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setTicker("TickerText:" + "您有新短消息,请注意查收!")
.setContentTitle("Notification Title")
.setContentText("This is the notification message")
.setContentIntent(pi).build();
notify.flags |= Notification.FLAG_AUTO_CANCEL; // FLAG_AUTO_CANCEL表明当通知被用户点击时,通知将被清除。
nm.notify(NotiID, notify);
如果要支持老的sdk,点击该通知后启动HtmlNavActivity
Intent intent = new Intent();
intent.setClass(mContext, HtmlNavActivity.class);
final PendingIntent pi = PendingIntent.getActivity(mContext, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
//创建一个通知
Notification n = new Notification(R.drawable.icon, obj.optString("LocalNotifyTitle"), System.currentTimeMillis());
// 为通知添加数据
n.flags |= Notification.FLAG_AUTO_CANCEL;
n.setLatestEventInfo(mContext, "LocalNotifyTitle", "LocalNotifyContent", pi);
// 发送通知
NotificationManager nm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(NotiID, n);
notify.flags |= Notification.FLAG_AUTO_CANCEL; //表明点击后,通知自动消失
也可以通过NotificationManager.cancel(NofiID); 手动取消通知
以下为通知设置震动,响铃,还有其它用法就不举例
long[] vibrates = {0, 1000, 1000, 1000};
notification.vibrate = vibrates;
Uri soundUri = Uri.fromFile(new File("/system/media/audio/ringtones/
Basic_tone.ogg"));
notification.sound = soundUri;
注意:
通知一到,如果该应用已经启动了进程,那么使用当前进程。如果没有启动进程,那么就会启动进程,可以看到会调用application的oncreate方法。从ddms也可以看出,通知一到,如果没有进程就启动该应用的进程
Date
Date date = new Date(System.currentTimeMillis());
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
Log.d("LiaBin", "current: " + dateFormat.format(date));
2. yyyy:年
3. MM:月
4. dd:日
5. hh:1~12小时制(1-12)
6. HH:24小时制(0-23)
7. mm:分
8. ss:秒
Calendar
推荐使用Calendar类进行时间和日期处理
Calendar checkInCal = Calendar.getInstance();
checkInCal.setTimeInMillis(System.currentTimeMillis());
checkInCal.add(Calendar.DATE, 2);
checkInCal.set(Calendar.HOUR_OF_DAY, 8);
if (checkInCal.getTimeInMillis() > System.currentTimeMillis()) {
}
getTime方法 该方法的作用是将Calendar类型的对象转换为对应的Date类对象
getTimeInMillis 转换为相对于1970.1.1时间
after方法 该方法的作用是判断当前日期对象是否在when对象的后面,如果在when对象的后面则返回true,否则返回false
Calendar.MONTH——月份
Calendar.DATE——日期
Calendar.DAY_OF_MONTH——日期,和上面的字段完全相同
Calendar.HOUR——12小时制的小时数
Calendar.HOUR_OF_DAY——24小时制的小时数
Calendar.MINUTE——分钟
Calendar.SECOND——秒
Calendar.DAY_OF_WEEK——星期几
案例
实际项目中经常会需要在某个时间点弹出通知,或者重复在某个时间点弹出通知,下面是代码实现。由于手机关机或者重启,闹钟就失效,所以在application中开启服务,service的oncreate方法中开启闹钟,所以每次启动进程,都会重新设定闹钟。
public class BaseApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
startService(new Intent(this, MyService.class));
Log.d("LiaBin","application oncreate");
}
}
public class MyService extends Service {
private final static String ACTION_NOTIFICATION = "ACTION_NOTIFICATION";
@Override
public void onCreate() {
super.onCreate();
initAlarm(ACTION_NOTIFICATION);
}
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
if (intent != null) {
String action = intent.getAction();
if (ACTION_NOTIFICATION.equals(action)) {
Intent acIntent = new Intent(this, NextActivity.class);
NotificationManager nm = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
PendingIntent pi = PendingIntent.getActivity(this, 0, acIntent, 0);
Notification notify = new Notification.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setTicker("TickerText:" + "您有新短消息,请注意查收!")
.setContentTitle("Notification Title")
.setContentText("This is the notification message")
.setContentIntent(pi).build();
notify.flags |= Notification.FLAG_AUTO_CANCEL; // FLAG_AUTO_CANCEL表明当通知被用户点击时,通知将被清除。
nm.notify(1000, notify);
}
}
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
private void initAlarm(String action) {
AlarmManager mAlarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent serviceIntent = new Intent(this, MyService.class);
serviceIntent.setAction(action);
PendingIntent pi = PendingIntent.getService(this, 0, serviceIntent, 0);
mAlarmManager.cancel(pi);
Calendar checkInCal = Calendar.getInstance();
checkInCal.setTimeInMillis(System.currentTimeMillis());
checkInCal.add(Calendar.SECOND, 5);
long repeat = 10000;
mAlarmManager.setRepeating(AlarmManager.RTC, checkInCal.getTimeInMillis(), repeat, pi);
}
}
开启进程就启动service,oncreate方法中开启闹钟,注意设置闹钟前必须把以前设置的闹钟取消,mAlarmManager.cancel(pi);很关键。
此时设置一个重复性闹钟,5秒后开始执行,之后10s重复一次。然后pendingintent还是该service,这样就不必额外写一个broadcast了,如果该service
内存中已经存在一个实例,那么只会调用onstart方法,然后判断action,显示notification通知即可。