游戏中恢复体力,玩法出现等功能都是定时的,需要每天定时推送,提醒玩家,最近遇到这个需求,也是花费了几天,记录一下。
部分代码参照https://www.jianshu.com/p/b9eb60f04f82,基本能实现推送显示功能,但是点击事件,第二篇写了一大堆,感觉用不到,就借鉴其他的。
首先是创建定时任务,写到mainactivity里面,供unity调用。
/**
* 显示数秒后的通知
*
* @param pAppName 应用名
* @param pTitle 通知标题
* @param pContent 通知内容
* @param pDelaySecond 延迟时间
* @param pIsDailyLoop 是否每日自动推送
* @throws IllegalArgumentException 错误信息
* @throws requestCode 推送分类
*/
public void ShowNotification(String pAppName, String pTitle, String pContent, int pDelaySecond, boolean pIsDailyLoop,int requestCode) throws IllegalArgumentException {
if(pDelaySecond < 0)
{
throw new IllegalArgumentException("The param: pDelaySecond < 0");
}
Intent intent = new Intent(this,AndroidNotificator.class);
intent.putExtra("appname", pAppName);
intent.putExtra("title", pTitle);
intent.putExtra("content", pContent);
intent.setAction("UNITY_NOTIFICATOR");
PendingIntent pi = PendingIntent.getBroadcast(this, requestCode, intent, 0);
AlarmManager am = (AlarmManager)this.getSystemService(Context.ALARM_SERVICE);
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND, pDelaySecond);
long alarmTime = calendar.getTimeInMillis();
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.M)
{
am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,alarmTime,pi);
}
else if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT)
{
am.setExact(AlarmManager.RTC_WAKEUP,alarmTime,pi);
}
else
{
am.set(AlarmManager.RTC_WAKEUP, alarmTime,pi);
}
}
参数如注释所述,其中pIsDailyLoop这个参数我没有使用,通过每日都定时一个推送来实现的。解释下代码,首先创建一个intent用来传输数据,构造函数里面第一个参数用的this,第二个参数传的是一个继承BroadcastReceiver的接收通知类。(我觉得这个参数好像并没有什么用,因为下文指定的有action,但是一直没记得去实验一下,就先这样了),在intent里面写入数据,为通知栏中显示的标题和内容。然后指定action,action是自定义的,作用为指定可以接收这个通知的接收器,在manifest文件中配置。然后创建一个pendingintent,携带上面创建的intent的数据,用来传输,之所以用getBroadcast是因为这个是要创建一个广播,第二个参数比较重要,不同类型的推送必须要设置不同的requestCode,相同code的推送,后面的会覆盖掉前面的,比如先创建了推送a,后创建推送b,如果设置相同的code,那么推送a将不会显示,这个比较坑,注意一下。接下来获取系统的定时器,创建定时任务,注意不同的版本创建定时任务要用不同的api,不然会导致android8.0定时不准,或者实时失效的情况,谨记。
接下来创建广播接收器,显示通知,类名即上面创建intent指定的类名,继承BroadcastReceiver。重写onReceive,显示通知。
public class AndroidNotificator extends BroadcastReceiver {
private static int m_nLastID = 0;
@SuppressWarnings("deprecation")
public void onReceive(Context pContext, Intent pIntent) {
ApplicationInfo applicationInfo = null;
PackageManager pm = pContext.getPackageManager();
try {
applicationInfo = pm.getApplicationInfo(pContext.getPackageName(), PackageManager.GET_META_DATA);
} catch (Exception ex) {
ex.printStackTrace();
return;
}
Intent intent = new Intent(pContext, NotifivationReceiver.class);
intent.setAction("UNITY_NOTIFRECEIVE");
PendingIntent contentIntent = PendingIntent.getBroadcast(pContext,0,intent,0);
Bundle bundle = pIntent.getExtras();
Notification.Builder builder = new Notification.Builder(pContext)
.setContentTitle((String)bundle.get("title"))
.setContentText((String)bundle.get("content"))
.setSmallIcon(R.drawable.app_icon)
.setContentIntent(contentIntent)
.setDefaults(Notification.DEFAULT_ALL)
.setPriority(Notification.PRIORITY_DEFAULT)
.setWhen(System.currentTimeMillis())
.setAutoCancel(true)
.setOngoing(false);
Notification notification=builder.getNotification();
NotificationManager nm = (NotificationManager)pContext.getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(m_nLastID, notification);
m_nLastID++;
}
}
前面那个try catch我不清楚有没有用,就保留下来了。先创建一个intent,传入另一个继承BroadcastReceiver的类,用作接收通知点击事件,action同样是自定义,指向同一个类。然后创建pendingintent,因为点击事件是立即的,不存在覆盖的问题,所以这个requestCode都传0,。接下来就是创建通知Notification,并设置各个属性,这个没什么好说的了,也没有什么坑。然后通知的显示是获取系统的通知管理器,调用api显示即可。这里面功能比较简单,就此略过。
最后创建另一个接收器,接收通知的点击事件,类名如上NotifivationReceiver,同样继承BroadcastReceiver,重写onReceive,响应点击。
public class NotifivationReceiver extends BroadcastReceiver {
@SuppressWarnings("deprecation")
public void onReceive(Context context, Intent pIntent) {
Log.d("NotifivationReceiver","receive");
Intent mainIntent = new Intent(context, MainActivity.class); mainIntent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED|Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(mainIntent);
}
}
注意一定要设置这样的flags,不然会出问题。这样设置之后,如果游戏正在前台,点击直接进入游戏,如果在后台,点击直接进入游戏,从后台变前台,不会重新启动,游戏已经停止,点击会启动游戏,因为我这个是unity项目,只有mainactivity一个activity。
最后贴一下manifest里面这两个类的配置。可以看到action是相对应的。
<receiver android:name="****.AndroidNotificator">
<intent-filter>
<action android:name="UNITY_NOTIFICATOR" />
</intent-filter>
</receiver>
<receiver android:name="****.NotifivationReceiver">
<intent-filter>
<action android:name="UNITY_NOTIFRECEIVE" />
</intent-filter>
</receiver>
到这个整个功能就完结了,每天的定时推送是我根据本地的数据来设置的,如果进游戏的时候发现今天已定时,就不会重复定时,如果没有,就定时一下,同时存入本地一个标识符用来判断,个人觉得甚至可以每次进入游戏都创建定时任务,因为相同的requestCode,后面的会覆盖前面的,不会出现重复通知的情况。
总结一下,有几个坑需要注意:
1.创建pendingintent的requestCode,不同的推送要设置不同的值
2.系统定时管理器的api,不同版本要使用不同的api
3.响应通知栏点击,因为也相当于广播,所以创建的intent要指定接收器
4.android4.4经测试,正常退出,强杀程序定时推送正常,android8.0,如果正常退出游戏,在正在运行的程序列表是能看到游戏,这样的话定时任务正常执行,但是如果通过正在运行的程序列表直接强杀程序,定时任务就不会执行,因为8.0杀得太干净了。