接下來我要教大家如何實(shí)現(xiàn)一個(gè)APP后臺的定點(diǎn)提醒Service,功能包括輸入時(shí)間后長期在后臺運(yùn)行,到達(dá)提醒時(shí)間時(shí),發(fā)送Notification手機(jī)通知欄,提醒用戶。
實(shí)現(xiàn)原理:
將需要提醒的內(nèi)容及時(shí)間存入數(shù)據(jù)庫中,啟動(dòng)Service時(shí)檢測數(shù)據(jù)庫是否有未提醒的時(shí)間,遍歷數(shù)據(jù)庫,從第一條最近的提醒時(shí)間開始,發(fā)送時(shí)間給手機(jī)系統(tǒng)鬧鐘,再定義一個(gè)廣播接收器,接收系統(tǒng)鬧鐘到點(diǎn)后的提醒。再發(fā)送Notification提醒用戶,需要的做的事。
步驟一#
創(chuàng)建SQLite數(shù)據(jù)庫
/**
*緩存數(shù)據(jù)的SQLite
* 2016/9/27.
*/
public class AppSQLiteData extends SQLiteOpenHelper {
public static String Remind_data="create table Remind_data("
+"id integer primary key autoincrement, "
+"remindTime long,"
+"content text,"
+"title text"+")";
private Context mContext;
public AppSQLiteData(Context context,String name,SQLiteDatabase.CursorFactory factory, intversion) {
super(context,name,factory,version);
mContext= context;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(Remind_data);
}
@Override
public void onUpgrade(SQLiteDatabase db, intoldVersion, intnewVersion) {
}
}
這里建了一個(gè)表,里面存入三個(gè)提醒時(shí)需要的信息,分別是時(shí)間,內(nèi)容,跟標(biāo)題。時(shí)間我采用的是毫秒型,所以存入的Long型。然后向數(shù)據(jù)庫添加我們需要提醒的時(shí)間跟內(nèi)容。
private AppSQLiteData mRemindSQL;
public void AddData(final Context context, final List<RemindList> data) {
//將獲取到的便簽列表時(shí)間緩存入SQL
new Thread(new Runnable() {
@Override
public void run() {
Calendar c = Calendar.getInstance();//獲取當(dāng)前時(shí)間,為了判斷添加時(shí)間是否已經(jīng)過時(shí)
mRemindSQL= new AppSQLiteData(context, "Remind.db", null, 1);
mRemindSQL.getWritableDatabase();
SQLiteDatabase db = mRemindSQL.getWritableDatabase();
ContentValues values = new ContentValues();
for (int i = 0; i < data.size() - 1; i++) {
if (!data.get(i).getRemindTime().equals("") && Long.parseLong(data.get(i).getRemindTime()) > c.getTimeInMillis()) {
values.put("remindTime", Long.parseLong(data.get(i).getRemindTime()));
values.put("title", data.get(i).getTitle());
values.put("content", data.get(i).getLevel());
db.insert("Remind_data", null, values);
}
values.clear();
}}).start();
需要注意的是我這里的List是接收的一個(gè)實(shí)體類,你們可以手動(dòng)添加data數(shù)據(jù),或者自定義實(shí)體類,再傳入數(shù)據(jù),方法名請自行修改。if是判斷時(shí)間是否大于當(dāng)前時(shí)間,否則不添加。
步驟二#
創(chuàng)建Service,后臺處理提醒的數(shù)據(jù)。
public class AlarmService extends Service {
private AlarmManager am;
private PendingIntent pi;
private Long time;
private String title;
private String content;
private AppSQLiteData mRemindSQL;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
getAlarmTime();
return START_REDELIVER_INTENT; } //這里為了提高優(yōu)先級,選擇START_REDELIVER_INTENT 沒那么容易被內(nèi)存清理時(shí)殺死
@Override
public void onDestroy() {
super.onDestroy();
}
public void getAlarmTime() {
mRemindSQL= new AppSQLiteData(this,"Remind.db", null, 1);
SQLiteDatabase db = mRemindSQL.getWritableDatabase();
Cursor cursor = db.query("Remind_data", null, null, null, null, null, null);
if (cursor.moveToFirst()) { //遍歷數(shù)據(jù)庫的表,拿出一條,選擇最近的時(shí)間賦值,作為第一條提醒數(shù)據(jù)。
time = cursor.getLong(cursor.getColumnIndex("remindTime"));
title = cursor.getString(cursor.getColumnIndex("title"));
content = cursor.getString(cursor.getColumnIndex("content"));
do { if (time > cursor.getLong(cursor.getColumnIndex("remindTime"))) {
time = cursor.getLong(cursor.getColumnIndex("remindTime"));
title = cursor.getString(cursor.getColumnIndex("title"));
content = cursor.getString(cursor.getColumnIndex("content"));
}
} while (cursor.moveToNext());
} else {
time = null;
}
db.delete("Remind_data", "remindTime=?", new String[]{String.valueOf(time)}); //刪除已經(jīng)發(fā)送提醒的時(shí)間
cursor.close(); //記得關(guān)閉游標(biāo),防止內(nèi)存泄漏
Intent startNotification = new Intent(this, AlarmReceiver.class); //這里啟動(dòng)的廣播,下一步會教大家設(shè)置
startNotification.putExtra("title", title);
startNotification.putExtra("content", content);
am = (AlarmManager) getSystemService(ALARM_SERVICE); //這里是系統(tǒng)鬧鐘的對象
pi = PendingIntent.getBroadcast(this, 0, startNotification, PendingIntent.FLAG_UPDATE_CURRENT); //設(shè)置事件
if (time != null) {
am.set(AlarmManager.RTC_WAKEUP, time, pi); //提交事件,發(fā)送給 廣播接收器
} else {
//當(dāng)提醒時(shí)間為空的時(shí)候,關(guān)閉服務(wù),下次添加提醒時(shí)再開啟
stopService(new Intent(this, AlarmService.class));
}
}}
因?yàn)槭情L期運(yùn)行在后臺,所以沒有與Activity進(jìn)行綁定操作,這里做的就是讀取數(shù)據(jù)庫的數(shù)據(jù),然后一條一條啟動(dòng)鬧鐘事件,到點(diǎn)后發(fā)送廣播給BroadcastReceiver,提醒完成后,再次回調(diào)服務(wù),開啟下一條提醒,直到?jīng)]有提醒時(shí)關(guān)閉。
一定要記得在AndroidManifest中配置Service,這里我取名為AlarmService。優(yōu)先級設(shè)置最大,防止被內(nèi)存回收銷毀。

步驟三#
設(shè)置BroadcastReceiver廣播接收器,接收系統(tǒng)鬧鐘發(fā)送過來的廣播。實(shí)現(xiàn)提醒業(yè)務(wù),這里我采用的是Notification通知,可根據(jù)自己需求更改。
public class AlarmReceiver extends BroadcastReceiver {
private NotificationManager manager;
private static final int NOTIFICATION_ID_1 = 0x00113;
private String title;
private String content = "提醒的時(shí)間到啦,快看看你要做的事...";
@Override
public void onReceive(Context context, Intent intent) {
//此處接收鬧鐘時(shí)間發(fā)送過來的廣播信息,為了方便設(shè)置提醒內(nèi)容
title = intent.getStringExtra("title");
content = intent.getStringExtra("content ");
showNormal(context);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setClass(context, AlarmService.class);
context.startService(intent); //回調(diào)Service,同一個(gè)Service只會啟動(dòng)一個(gè),所以直接再次啟動(dòng)Service,會重置開啟新的提醒,
} /** * 發(fā)送通知 */
private void showNormal(Context context) {
Intent intent = new Intent(context, BianQianDataActivity.class);//這里是點(diǎn)擊Notification 跳轉(zhuǎn)的界面,可以自己選擇
PendingIntent pi = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new NotificationCompat.Builder(context)
.setSmallIcon(R.mipmap.daka) //設(shè)置通知圖標(biāo)。
.setTicker(content) //通知時(shí)在狀態(tài)欄顯示的通知內(nèi)容
.setContentInfo("便簽提醒") //內(nèi)容信息
.setContentTitle(title) //設(shè)置通知標(biāo)題。
.setContentText(content) //設(shè)置通知內(nèi)容。
.setAutoCancel(true) //點(diǎn)擊通知后通知消失
.setDefaults(Notification.DEFAULT_ALL) //設(shè)置系統(tǒng)默認(rèn)的通知音樂、振動(dòng)、LED等。
.setContentIntent(pi)
.build();
manager.notify(NOTIFICATION_ID_1, notification);
}}
到這里,一個(gè)基本的定點(diǎn)提醒服務(wù)就完成了,可以根據(jù)需求,完成提醒后的業(yè)務(wù)邏輯,比如直接彈出應(yīng)用等。在存入提醒的時(shí)間后,啟動(dòng)服務(wù)就行,另外Receiver和Service都需要在配置文件中注冊,不可忘記。
擴(kuò)展#
另外還可以根據(jù)個(gè)人需求,設(shè)置開機(jī)自啟動(dòng)該服務(wù),只需要監(jiān)聽系統(tǒng)廣播即可,我建議是開啟自啟動(dòng)的,否則關(guān)機(jī)后,有的提醒將無法完成。
//開機(jī)廣播接收
public class BootReceiver extends BroadcastReceiver {
public BootReceiver() { }
private final String ACTION = "android.intent.action.BOOT_COMPLETED";
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ACTION)) {
Intent inten2 = new Intent(context, AlarmService.class);
context.startService(inten2);
}
boolean isServiceRunning = false;
if (intent.getAction().equals(Intent.ACTION_TIME_TICK)) {
//檢查Service狀態(tài)
ActivityManager manager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service :manager.getRunningServices(Integer.MAX_VALUE)) {
if("so.xxxx.xxxxService".equals(service.service.getClassName())) {
isServiceRunning = true;
}
}
if (!isServiceRunning) {
Intent i = new Intent(context, AlarmService.class);
context.startService(i);
}
}
}}
在這里,我還實(shí)現(xiàn)了檢測服務(wù)是否被關(guān)閉,每五分鐘會監(jiān)聽到一個(gè)系統(tǒng)廣播,如果服務(wù)被關(guān)閉,就會再次啟動(dòng)。當(dāng)然,這個(gè)定點(diǎn)提醒做到這里,一般的情況下已經(jīng)有點(diǎn)死皮賴臉難以被殺死,你如果還想更流氓一點(diǎn),就去看一下守護(hù)進(jìn)程的相關(guān)知識,
鏈接
守護(hù)進(jìn)程相關(guān)知識
結(jié)語#
剛開始用簡書寫文章,還不太習(xí)慣,代碼還是自己一行一行的排版,不太好看,大家如果有什么問題,或者我代碼中有什么錯(cuò)誤,歡迎大家指出,定為修改,不懂的也可以提問,,謝謝!
本文為原創(chuàng)文章,未經(jīng)允許不得轉(zhuǎn)載