接下來我要教大家如何實現(xiàn)一個APP后臺的定點提醒Service,功能包括輸入時間后長期在后臺運行,到達提醒時間時,發(fā)送Notification手機通知欄,提醒用戶。
實現(xiàn)原理:
將需要提醒的內(nèi)容及時間存入數(shù)據(jù)庫中,啟動Service時檢測數(shù)據(jù)庫是否有未提醒的時間,遍歷數(shù)據(jù)庫,從第一條最近的提醒時間開始,發(fā)送時間給手機系統(tǒng)鬧鐘,再定義一個廣播接收器,接收系統(tǒng)鬧鐘到點后的提醒。再發(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) {
}
}
這里建了一個表,里面存入三個提醒時需要的信息,分別是時間,內(nèi)容,跟標(biāo)題。時間我采用的是毫秒型,所以存入的Long型。然后向數(shù)據(jù)庫添加我們需要提醒的時間跟內(nèi)容。
private AppSQLiteData mRemindSQL;
public void AddData(final Context context, final List<RemindList> data) {
//將獲取到的便簽列表時間緩存入SQL
new Thread(new Runnable() {
@Override
public void run() {
Calendar c = Calendar.getInstance();//獲取當(dāng)前時間,為了判斷添加時間是否已經(jīng)過時
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是接收的一個實體類,你們可以手動添加data數(shù)據(jù),或者自定義實體類,再傳入數(shù)據(jù),方法名請自行修改。if是判斷時間是否大于當(dāng)前時間,否則不添加。
步驟二#
創(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)存清理時殺死
@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ù)據(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ā)送提醒的時間
cursor.close(); //記得關(guān)閉游標(biāo),防止內(nèi)存泄漏
Intent startNotification = new Intent(this, AlarmReceiver.class); //這里啟動的廣播,下一步會教大家設(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)提醒時間為空的時候,關(guān)閉服務(wù),下次添加提醒時再開啟
stopService(new Intent(this, AlarmService.class));
}
}}
因為是長期運行在后臺,所以沒有與Activity進行綁定操作,這里做的就是讀取數(shù)據(jù)庫的數(shù)據(jù),然后一條一條啟動鬧鐘事件,到點后發(fā)送廣播給BroadcastReceiver,提醒完成后,再次回調(diào)服務(wù),開啟下一條提醒,直到?jīng)]有提醒時關(guān)閉。
一定要記得在AndroidManifest中配置Service,這里我取名為AlarmService。優(yōu)先級設(shè)置最大,防止被內(nèi)存回收銷毀。
步驟三#
設(shè)置BroadcastReceiver廣播接收器,接收系統(tǒng)鬧鐘發(fā)送過來的廣播。實現(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 = "提醒的時間到啦,快看看你要做的事...";
@Override
public void onReceive(Context context, Intent intent) {
//此處接收鬧鐘時間發(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,同一個Service只會啟動一個,所以直接再次啟動Service,會重置開啟新的提醒,
} /** * 發(fā)送通知 */
private void showNormal(Context context) {
Intent intent = new Intent(context, BianQianDataActivity.class);//這里是點擊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) //通知時在狀態(tài)欄顯示的通知內(nèi)容
.setContentInfo("便簽提醒") //內(nèi)容信息
.setContentTitle(title) //設(shè)置通知標(biāo)題。
.setContentText(content) //設(shè)置通知內(nèi)容。
.setAutoCancel(true) //點擊通知后通知消失
.setDefaults(Notification.DEFAULT_ALL) //設(shè)置系統(tǒng)默認(rèn)的通知音樂、振動、LED等。
.setContentIntent(pi)
.build();
manager.notify(NOTIFICATION_ID_1, notification);
}}
到這里,一個基本的定點提醒服務(wù)就完成了,可以根據(jù)需求,完成提醒后的業(yè)務(wù)邏輯,比如直接彈出應(yīng)用等。在存入提醒的時間后,啟動服務(wù)就行,另外Receiver和Service都需要在配置文件中注冊,不可忘記。
擴展#
另外還可以根據(jù)個人需求,設(shè)置開機自啟動該服務(wù),只需要監(jiān)聽系統(tǒng)廣播即可,我建議是開啟自啟動的,否則關(guān)機后,有的提醒將無法完成。
//開機廣播接收
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);
}
}
}}
在這里,我還實現(xiàn)了檢測服務(wù)是否被關(guān)閉,每五分鐘會監(jiān)聽到一個系統(tǒng)廣播,如果服務(wù)被關(guān)閉,就會再次啟動。當(dāng)然,這個定點提醒做到這里,一般的情況下已經(jīng)有點死皮賴臉難以被殺死,你如果還想更流氓一點,就去看一下守護進程的相關(guān)知識,
鏈接
守護進程相關(guān)知識
結(jié)語#
剛開始用簡書寫文章,還不太習(xí)慣,代碼還是自己一行一行的排版,不太好看,大家如果有什么問題,或者我代碼中有什么錯誤,歡迎大家指出,定為修改,不懂的也可以提問,,謝謝!
本文為原創(chuàng)文章,未經(jīng)允許不得轉(zhuǎn)載