首先寫幾點(diǎn)感悟:
- 做兼容真的很累很費(fèi)勁~
- android 8.0 廣播部分不再支持動(dòng)態(tài)注冊(cè)一忱,所以應(yīng)該用service來(lái)實(shí)現(xiàn)定時(shí)推送功能
- 無(wú)論是鬧鐘還是通知寂恬,都得做兼容處理弟灼,android 8.0通知必須加channel_id邑飒,否則通知無(wú)法顯示
- 查閱大量資料喊暖,發(fā)現(xiàn)代碼都參差不齊辜王,不過(guò)還是有很多值得參考的地方劈狐,目前這份代碼有很多都是抄字那些博主的文章,然后稍加改動(dòng)呐馆,加以整合而成
- 代碼分為三個(gè)類懈息,service類、鬧鐘工具類和通知工具類
首先摹恰,鬧鐘工具類:
package com.util;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import java.io.Serializable;
import java.util.Map;
/**
* 鬧鐘定時(shí)工具類
*/
public class AlarmTimerUtil {
/**
* 設(shè)置定時(shí)鬧鐘
*
* @param context
* @param alarmId
* @param action
* @param map 要傳遞的參數(shù)
*/
public static void setAlarmTimer(Context context, int alarmId, long time, String action,Map<String,Serializable> map) {
Intent myIntent = new Intent();
myIntent.setAction(action);
if(map != null){
for (String key: map.keySet()) {
myIntent.putExtra(key,map.get(key));
}
}
// PendingIntent sender = PendingIntent.getBroadcast(context, alarmId, myIntent, 0);//如果是廣播辫继,就這么寫
PendingIntent sender = PendingIntent.getService(context, alarmId, myIntent, 0);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { //MARSHMALLOW OR ABOVE
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, time, sender);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { //LOLLIPOP 21 OR ABOVE
AlarmManager.AlarmClockInfo alarmClockInfo = new AlarmManager.AlarmClockInfo(time, sender);
alarmManager.setAlarmClock(alarmClockInfo, sender);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { //KITKAT 19 OR ABOVE
alarmManager.setExact(AlarmManager.RTC_WAKEUP, time, sender);
} else { //FOR BELOW KITKAT ALL DEVICES
alarmManager.set(AlarmManager.RTC_WAKEUP, time, sender);
}
}
/**
* 取消鬧鐘
* @param context
* @param action
*/
public static void cancelAlarmTimer(Context context, String action,int alarmId) {
Intent myIntent = new Intent();
myIntent.setAction(action);
// PendingIntent sender = PendingIntent.getBroadcast(context, alarmId, myIntent, 0);//如果是廣播怒见,就這么寫
PendingIntent sender = PendingIntent.getService(context, alarmId, myIntent,0);
AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarm.cancel(sender);
}
}
鬧鐘類,目前只用過(guò)AlarmManager.RTC_WAKEUP
類型姑宽,這個(gè)是精確定時(shí)遣耍,很多博客都提到過(guò),不了解的可以自己查查炮车。然后action
用來(lái)啟動(dòng)服務(wù)或者廣播舵变,alarmId
就是requestCode
,用來(lái)區(qū)別不同的鬧鐘瘦穆。該工具類不僅僅可以用來(lái)定時(shí)通知纪隙,只要稍加改動(dòng),定時(shí)廣播扛或、定時(shí)任務(wù)绵咱、定時(shí)彈窗都是可以做的。
通知工具類
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Build;
import android.support.annotation.DrawableRes;
import android.support.v7.app.NotificationCompat;
import android.util.Log;
import com.example.MainActivity;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
class NotifyObject implements Serializable{
public Integer type;
public String title;
public String subText;
public String content;
public String param;
public Long firstTime;
public Class<? extends Activity> activityClass;
@DrawableRes public int icon;
public List<Long> times = new ArrayList<>();
public static byte[] toBytes(NotifyObject obj) throws IOException{
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream oos = null;
String content = null;
oos = new ObjectOutputStream(bout);
oos.writeObject(obj);
oos.close();
byte[] bytes = bout.toByteArray();
bout.close();
return bytes;
}
public static NotifyObject from(String content) throws IOException, ClassNotFoundException {
ByteArrayInputStream bin = new ByteArrayInputStream(content.getBytes("ISO-8859-1"));
ObjectInputStream ois = null;
NotifyObject obj = null;
ois = new ObjectInputStream(bin);
obj = (NotifyObject)ois.readObject();
ois.close();
bin.close();
return obj;
}
public static String to(NotifyObject obj) throws IOException{
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream oos = null;
String content = null;
oos = new ObjectOutputStream(bout);
oos.writeObject(obj);
oos.close();
byte[] bytes = bout.toByteArray();
content = new String(bytes,"ISO-8859-1");
bout.close();
return content;
}
}
/**
* 消息通知工具
*/
public class NotificationUtil {
private static final String TAG = "NotificationUtil";
/**
* 通過(guò)定時(shí)鬧鐘發(fā)送通知
* @param context
* @param notifyObjectMap
*/
public static void notifyByAlarm(Context context,Map<Integer,NotifyObject> notifyObjectMap){
//將數(shù)據(jù)存儲(chǔ)起來(lái)
int count = 0;
NotificationManager manager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
Set<Integer> keySet = notifyObjectMap.keySet();
for (Integer key0: keySet) {
if(!notifyObjectMap.containsKey(key0)){
break;
}
NotifyObject obj = notifyObjectMap.get(key0);
if(obj == null){
break;
}
if(obj.times.size() <= 0){
if(obj.firstTime > 0){
try {
Map<String,Serializable> map = new HashMap<>();
map.put("KEY_NOTIFY_ID",obj.type);
map.put("KEY_NOTIFY",NotifyObject.to(obj));
AlarmTimerUtil.setAlarmTimer(context,++count,obj.firstTime,"TIMER_ACTION",map);
} catch (IOException e) {
e.printStackTrace();
}
}
}else{
for (long time: obj.times) {
if(time > 0){
try {
Map<String,Serializable> map = new HashMap<>();
map.put("KEY_NOTIFY_ID",obj.type);
map.put("KEY_NOTIFY",NotifyObject.to(obj));
AlarmTimerUtil.setAlarmTimer(context,++count,time,"TIMER_ACTION",map);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
SharedPreferences mPreferences = context.getSharedPreferences("SHARE_PREFERENCE_NOTIFICATION",Context.MODE_PRIVATE);
SharedPreferences.Editor edit = mPreferences.edit();
edit.putInt("KEY_MAX_ALARM_ID",count);
edit.commit();
}
public static void notifyByAlarmByReceiver(Context context,NotifyObject obj){
if(context == null || obj== null)return;
NotificationManager manager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
notifyMsg(context,obj,obj.type,System.currentTimeMillis(),manager);
}
/**
* 消息通知
* @param context
* @param obj
*/
private static void notifyMsg(Context context,NotifyObject obj,int nid,long time,NotificationManager mNotifyMgr){
if(context == null || obj == null)return;
if(mNotifyMgr == null){
mNotifyMgr = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
}
if(time <= 0)return;
//準(zhǔn)備intent
Intent intent = new Intent(context,obj.activityClass);
if(obj.param != null && obj.param.trim().length() > 0){
intent.putExtra("param",obj.param);
}
//notification
Notification notification = null;
String contentText = obj.content;
// 構(gòu)建 PendingIntent
PendingIntent pi = PendingIntent.getActivity(context, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
//版本兼容
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){//兼容Android8.0
String id ="my_channel_01";
int importance =NotificationManager.IMPORTANCE_LOW;
CharSequence name = "notice";
NotificationChannel mChannel =new NotificationChannel(id, name,importance);
mChannel.enableLights(true);
mChannel.setDescription("just show notice");
mChannel.enableLights(true);
mChannel.setLightColor(Color.GREEN);
mChannel.enableVibration(true);
mChannel.setVibrationPattern(new long[]{100,200,300,400,500,400,300,200,400});
mNotifyMgr.createNotificationChannel(mChannel);
Notification.Builder builder = new Notification.Builder(context,id);
builder.setAutoCancel(true)
.setContentIntent(pi)
.setContentTitle(obj.title)
.setContentText(obj.content)
.setOngoing(false)
.setSmallIcon(obj.icon)
.setWhen(System.currentTimeMillis());
if(obj.subText != null && obj.subText.trim().length() > 0){
builder.setSubText(obj.subText);
}
notification = builder.build();
}else if (Build.VERSION.SDK_INT >= 23) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setContentTitle(obj.title)
.setContentText(contentText)
.setSmallIcon(obj.icon)
.setContentIntent(pi)
.setAutoCancel(true)
.setOngoing(false)
.setWhen(System.currentTimeMillis());
if(obj.subText != null && obj.subText.trim().length() > 0){
builder.setSubText(obj.subText);
}
notification = builder.build();
} else if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN &&
Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
Notification.Builder builder = new Notification.Builder(context);
builder.setAutoCancel(true)
.setContentIntent(pi)
.setContentTitle(obj.title)
.setContentText(obj.content)
.setOngoing(false)
.setSmallIcon(obj.icon)
.setWhen(System.currentTimeMillis());
if(obj.subText != null && obj.subText.trim().length() > 0){
builder.setSubText(obj.subText);
}
notification = builder.build();
}
if(notification != null){
mNotifyMgr.notify(nid, notification);
}
}
/**
* 取消所有通知 同時(shí)取消定時(shí)鬧鐘
* @param context
*/
public static void clearAllNotifyMsg(Context context){
try{
NotificationManager mNotifyMgr =
(NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
mNotifyMgr.cancelAll();
SharedPreferences mPreferences = context.getSharedPreferences("SHARE_PREFERENCE_NOTIFICATION",Context.MODE_PRIVATE);
int max_id = mPreferences.getInt("KEY_MAX_ALARM_ID",0);
for (int i = 1;i <= max_id;i++){
AlarmTimerUtil.cancelAlarmTimer(context,"TIMER_ACTION",i);
}
//清除數(shù)據(jù)
mPreferences.edit().remove("KEY_MAX_ALARM_ID").commit();
}catch (Exception e){
Log.e(TAG,"取消通知失敗",e);
}
}
}
-
NotifyObject
實(shí)現(xiàn)了序列化熙兔,便于傳遞參數(shù)悲伶,然后Activity
類換成自己的Activity就行了。 -
clearAllNotifyMsg
用于清除所有通知住涉,同時(shí)清除所有鬧鐘麸锉。 -
notifyByAlarmByReceiver
無(wú)論是在廣播還是在服務(wù)中,都可以調(diào)用這個(gè)進(jìn)行立即通知 -
notifyByAlarm
在activity中調(diào)用改方法舆声,將開啟定時(shí)通知 -
notifyMsg
這個(gè)是真正實(shí)現(xiàn)通知的方法花沉,但并不需要外部調(diào)用
服務(wù)或者廣播類及其配置
服務(wù)和廣播配置一個(gè)就可以了,目前我才有的是服務(wù)的配置方法
服務(wù)的寫法
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import java.io.IOException;
public class AlarmService extends Service {
public AlarmService() {
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
String str = intent.getStringExtra("KEY_NOTIFY");
if(str == null || str.trim().length() == 0)return super.onStartCommand(intent, flags, startId);
try {
NotifyObject obj = NotifyObject.from(str);
NotificationUtil.notifyByAlarmByReceiver(this,obj);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return super.onStartCommand(intent, flags, startId);
}
}
服務(wù)的配置方法:
<service
android:name=".service.AlarmService"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="TIMER_ACTION" />
</intent-filter>
</service>
廣播的寫法
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import com.util.NotificationUtil;
import java.io.IOException;
public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if("TIMER_ACTION".equals(intent.getAction())){
String str = intent.getStringExtra("KEY_NOTIFY");
NotifyObject obj = null;
try {
obj = NotifyObject.from(str);
if(obj != null){
NotificationUtil.notifyByAlarmByReceiver(context,obj);//立即開啟通知
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
廣播的配置方法:
<receiver android:name=".service.AlarmReceiver">
<intent-filter>
<action android:name="TIMER_ACTION" />
</intent-filter>
</receiver>
由于查看的資料太多了媳握,所以就不一一列舉了主穗,然后提供一個(gè)測(cè)試方法,可以在MainActivity
的OnCreate
方法中調(diào)用:
long now = System.currentTimeMillis();
long interval[] = {0,10,60,3000,6000,12000,30000,50000,60000,100000};
int count = 1;
SimpleDateFormat smf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
for (long inter:interval) {
Date date = new Date(now+inter);
NotifyObject obj = new NotifyObject();
...
/**
type:count++,
title:"標(biāo)題",
subText:"理論提醒時(shí)間:"+smf.format(date),
content:"類型:"+(count-1)+","+inter,
"",
firstTime:now+inter,
-1l,
null
//賦值就自己賦值啦~
**/
...
notifyObjects.put(obj.type,obj);
}
NotificationUtil.notifyByAlarm(this,this.notifyObjects);