android 實(shí)現(xiàn)本地定時(shí)推送(兼容)

首先寫幾點(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è)試方法,可以在MainActivityOnCreate方法中調(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);
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末毙芜,一起剝皮案震驚了整個(gè)濱河市忽媒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌腋粥,老刑警劉巖晦雨,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異隘冲,居然都是意外死亡闹瞧,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門展辞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)奥邮,“玉大人,你說(shuō)我怎么就攤上這事∏⑾伲” “怎么了脚粟?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蘸朋。 經(jīng)常有香客問我核无,道長(zhǎng),這世上最難降的妖魔是什么藕坯? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任团南,我火速辦了婚禮,結(jié)果婚禮上炼彪,老公的妹妹穿的比我還像新娘吐根。我一直安慰自己,他們只是感情好辐马,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布拷橘。 她就那樣靜靜地躺著,像睡著了一般齐疙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上旭咽,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天贞奋,我揣著相機(jī)與錄音,去河邊找鬼穷绵。 笑死轿塔,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的仲墨。 我是一名探鬼主播勾缭,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼目养!你這毒婦竟也來(lái)了俩由?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤癌蚁,失蹤者是張志新(化名)和其女友劉穎幻梯,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體努释,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡碘梢,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了伐蒂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片煞躬。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出恩沛,到底是詐尸還是另有隱情在扰,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布复唤,位于F島的核電站健田,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏佛纫。R本人自食惡果不足惜妓局,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望呈宇。 院中可真熱鬧好爬,春花似錦、人聲如沸甥啄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蜈漓。三九已至穆桂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間融虽,已是汗流浹背享完。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留有额,地道東北人般又。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像巍佑,于是被迫代替她去往敵國(guó)和親茴迁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,116評(píng)論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理萤衰,服務(wù)發(fā)現(xiàn)堕义,斷路器,智...
    卡卡羅2017閱讀 134,656評(píng)論 18 139
  • 【Android Activity】 什么是 Activity? 四大組件之一,通常一個(gè)用戶交互界面對(duì)應(yīng)一個(gè) ac...
    Rtia閱讀 3,805評(píng)論 3 18
  • 激昂的音樂就在耳旁 無(wú)數(shù)的理由催著你堅(jiān)強(qiáng) 倒下去的 被稱作榜樣 站著的 還未來(lái)得及俯身?yè)肀?就要奔赴另一個(gè)戰(zhàn)場(chǎng) ...
    雷聲狂閱讀 393評(píng)論 1 3
  • 簡(jiǎn)介 需要導(dǎo)入頭文件#import <LocalAuthentication/LocalAuthenticatio...
    門前有棵葡萄樹閱讀 1,584評(píng)論 0 4