接下来我要教大家如何实现一个APP后台的定点提醒Service,功能包括输入时间后长期在后台运行,到达提醒时间时,发送Notification手机通知栏,提醒用户。
实现原理:
将需要提醒的内容及时间存入数据库中,启动Service时检测数据库是否有未提醒的时间,遍历数据库,从第一条最近的提醒时间开始,发送时间给手机系统闹钟,再定义一个广播接收器,接收系统闹钟到点后的提醒。再发送Notification提醒用户,需要的做的事。
步骤一#
创建SQLite数据库
/**
*缓存数据的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) {
}
}
这里建了一个表,里面存入三个提醒时需要的信息,分别是时间,内容,跟标题。时间我采用的是毫秒型,所以存入的Long型。然后向数据库添加我们需要提醒的时间跟内容。
private AppSQLiteData mRemindSQL;
public void AddData(final Context context, final List data) {
//将获取到的便签列表时间缓存入SQL
new Thread(new Runnable() {
@Override
public void run() {
Calendar c = Calendar.getInstance();//获取当前时间,为了判断添加时间是否已经过时
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数据,或者自定义实体类,再传入数据,方法名请自行修改。if是判断时间是否大于当前时间,否则不添加。
步骤二#
创建Service,后台处理提醒的数据。
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; } //这里为了提高优先级,选择START_REDELIVER_INTENT 没那么容易被内存清理时杀死
@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()) { //遍历数据库的表,拿出一条,选择最近的时间赋值,作为第一条提醒数据。
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)}); //删除已经发送提醒的时间
cursor.close(); //记得关闭游标,防止内存泄漏
Intent startNotification = new Intent(this, AlarmReceiver.class); //这里启动的广播,下一步会教大家设置
startNotification.putExtra("title", title);
startNotification.putExtra("content", content);
am = (AlarmManager) getSystemService(ALARM_SERVICE); //这里是系统闹钟的对象
pi = PendingIntent.getBroadcast(this, 0, startNotification, PendingIntent.FLAG_UPDATE_CURRENT); //设置事件
if (time != null) {
am.set(AlarmManager.RTC_WAKEUP, time, pi); //提交事件,发送给 广播接收器
} else {
//当提醒时间为空的时候,关闭服务,下次添加提醒时再开启
stopService(new Intent(this, AlarmService.class));
}
}}
因为是长期运行在后台,所以没有与Activity进行绑定操作,这里做的就是读取数据库的数据,然后一条一条启动闹钟事件,到点后发送广播给BroadcastReceiver,提醒完成后,再次回调服务,开启下一条提醒,直到没有提醒时关闭。
一定要记得在AndroidManifest中配置Service,这里我取名为AlarmService。优先级设置最大,防止被内存回收销毁。
步骤三#
设置BroadcastReceiver广播接收器,接收系统闹钟发送过来的广播。实现提醒业务,这里我采用的是Notification通知,可根据自己需求更改。
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) {
//此处接收闹钟时间发送过来的广播信息,为了方便设置提醒内容
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); //回调Service,同一个Service只会启动一个,所以直接再次启动Service,会重置开启新的提醒,
} /** * 发送通知 */
private void showNormal(Context context) {
Intent intent = new Intent(context, BianQianDataActivity.class);//这里是点击Notification 跳转的界面,可以自己选择
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) //设置通知图标。
.setTicker(content) //通知时在状态栏显示的通知内容
.setContentInfo("便签提醒") //内容信息
.setContentTitle(title) //设置通知标题。
.setContentText(content) //设置通知内容。
.setAutoCancel(true) //点击通知后通知消失
.setDefaults(Notification.DEFAULT_ALL) //设置系统默认的通知音乐、振动、LED等。
.setContentIntent(pi)
.build();
manager.notify(NOTIFICATION_ID_1, notification);
}}
到这里,一个基本的定点提醒服务就完成了,可以根据需求,完成提醒后的业务逻辑,比如直接弹出应用等。在存入提醒的时间后,启动服务就行,另外Receiver和Service都需要在配置文件中注册,不可忘记。
扩展#
另外还可以根据个人需求,设置开机自启动该服务,只需要监听系统广播即可,我建议是开启自启动的,否则关机后,有的提醒将无法完成。
//开机广播接收
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状态
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);
}
}
}}
在这里,我还实现了检测服务是否被关闭,每五分钟会监听到一个系统广播,如果服务被关闭,就会再次启动。当然,这个定点提醒做到这里,一般的情况下已经有点死皮赖脸难以被杀死,你如果还想更流氓一点,就去看一下守护进程的相关知识,
链接
守护进程相关知识
结语#
刚开始用写文章,还不太习惯,代码还是自己一行一行的排版,不太好看,大家如果有什么问题,或者我代码中有什么错误,欢迎大家指出,定为修改,不懂的也可以提问,,谢谢!
本文为原创文章,未经允许不得转载