前言
我们玩过很多游戏都有通知推送功能,如android和ios平台的游戏都有此功能。此功能的用途可谓十分重要,例如玩家久不登录游戏,我们可以在适当的时间内提醒玩家登录,进而吸引玩家回流。或者游戏有更新时,也可以通知玩家更新,让玩家能及时玩到更新的内容,进而提升玩家对游戏的粘度。下面我们就来实现unity的本地通知推送功能。
一、集成Unity sdk
打开包管理器,搜索 Mobile notifycations 组件安装,如图:
安装完毕后,在Packages 目录下会出现相关文件夹,如图:
安卓需要配置通知图标,Ios 不用配置,如图:
二、定义基类
由于我们要上ios 和 android 平台,如果只是利用组件直接写代码实现通知推送并不是十分明智的选择。更好的方案是对组件再包装一层,然后直接对第三方调用提供通用的接口,把平台的差异和复杂性在接口范围内解决掉。
首先,定义一个基类:
#if(NotifyLocal)
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Gamelogic
{
public class NotifyInfo
{
public string Title;
public string Text;
public int Day;
public int Hour;
public int Minute;
public int Second;
public string SamllIcon = "smaicon";
public string LargeIcon = "laricon";
public bool Repeats = true;
}
public class NotifyInfos
{
public List<NotifyInfo> Notifys = new List<NotifyInfo>();
}
public class NotifyGenerater
{
public int NotifyNumber { get; set; } = 0;
/// <summary>
/// 获取下一次通知发送的时间
/// </summary>
/// <param name="info"></param>
/// <returns></returns>
public DateTime GetNextTime(NotifyInfo info)
{
var now = DateTime.Now;
var nextt = now + new TimeSpan(info.Day, info.Hour, info.Minute, info.Second);
//23点后8点前不发通知
if (nextt.Hour >= 23)
nextt += new TimeSpan(9, 0, 0);
if (nextt.Hour <= 8)
nextt += new TimeSpan(8 - nextt.Hour, 0, 0);
return nextt;
}
public virtual void Dispose() { }
private void LogNotify(NotifyInfo info)
{
Debug.Log($"{nameof(NotifyInfo)}.{nameof(GenerateIntervalNotify)} {info.Day},{info.Hour},{info.Minute},{info.Second},{info.SamllIcon},{info.LargeIcon}");
Debug.Log($"{nameof(NotifyInfo)}.{nameof(GenerateIntervalNotify)} {info.Title}");
Debug.Log($"{nameof(NotifyInfo)}.{nameof(GenerateIntervalNotify)} {info.Text}");
}
/// <summary>
/// 生成间隔通知,会在间隔一定时间后触发
/// </summary>
/// <param name="info"></param>
public virtual void GenerateIntervalNotify(NotifyInfo info)
{
LogNotify(info);
}
/// <summary>
/// 生成日历通知,会在指定的下个时间点触发
/// </summary>
/// <param name="info"></param>
public virtual void GenerateCalendarNotify(NotifyInfo info)
{
LogNotify(info);
}
/// <summary>
/// 清除已经显示的通知
/// </summary>
public virtual void ClearAllDisplayed()
{
}
}
}
#endif
三、Android 本地通知推送实现
我们定义了基类后,也就是定义了对外通用的接口方法,接下来就是针对特定的平台实现。我们先来实现安卓平台的推送,代码如下:
#if(NotifyLocal)
#if UNITY_ANDROID
using System;
using System.Collections.Generic;
using Unity.Notifications.Android;
namespace Gamelogic
{
/// <summary>
/// 安桌本地通知发生器
/// </summary>
public class AndroidNotifyGenerater : NotifyGenerater
{
private string _channelId = "bubbleId";
public AndroidNotifyGenerater()
{
ResetNotify();
}
/// <summary>
/// 重设通知
/// </summary>
private void ResetNotify()
{
//清空计数、清空所有已注册的通知和channel
NotifyNumber = 0;
AndroidNotificationCenter.CancelAllNotifications();
AndroidNotificationCenter.DeleteNotificationChannel(_channelId);
//重新注册channel
var channel = new AndroidNotificationChannel();
channel.Id = _channelId;
channel.Name = "bubbleName";
channel.Importance = Importance.High;
channel.Description = "Generic notifications";
channel.CanShowBadge = true;
channel.EnableLights = true;
channel.LockScreenVisibility = LockScreenVisibility.Public;
AndroidNotificationCenter.RegisterNotificationChannel(channel);
}
public override void GenerateIntervalNotify(NotifyInfo info)
{
base.GenerateIntervalNotify(info);
var nextt = GetNextTime(info);
var notify = new AndroidNotification();
notify.Title = info.Title;
notify.Text = info.Text;
notify.FireTime = nextt;
notify.SmallIcon = info.SamllIcon;
notify.LargeIcon = info.LargeIcon;
notify.Number = NotifyNumber++;
notify.ShouldAutoCancel = true;
notify.RepeatInterval = TimeSpan.FromHours(1);
AndroidNotificationCenter.SendNotification(notify, _channelId);
}
public override void GenerateCalendarNotify(NotifyInfo info)
{
base.GenerateCalendarNotify(info);
}
public override void ClearAllDisplayed()
{
base.ClearAllDisplayed();
AndroidNotificationCenter.CancelAllDisplayedNotifications();
}
}
}
#endif
#endif
四、Ios 本地通知推送实现
代码如下:
#if (NotifyLocal)
#if !UNITY_IOS
using System;
using System.Collections.Generic;
using Unity.Notifications.iOS;
namespace Gamelogic
{
/// <summary>
/// Ios通知触发器
/// </summary>
public class IosNotifyGenerater : NotifyGenerater
{
public IosNotifyGenerater()
{
ResetNotify();
}
private void ResetNotify()
{
//清0,清空已显示的和已计划的
NotifyNumber = 0;
iOSNotificationCenter.ApplicationBadge = 0;
iOSNotificationCenter.RemoveAllDeliveredNotifications();
iOSNotificationCenter.RemoveAllScheduledNotifications();
}
public override void GenerateIntervalNotify(NotifyInfo info)
{
base.GenerateIntervalNotify(info);
var tt = new iOSNotificationTimeIntervalTrigger();//间隔通知触发器
tt.TimeInterval = new TimeSpan(info.Day, info.Hour, info.Minute, info.Second);
tt.Repeats = info.Repeats;
var notify = new iOSNotification();
notify.Title = info.Title;
notify.Body = info.Text;
notify.Badge = NotifyNumber++;
notify.ShowInForeground = true;
notify.ForegroundPresentationOption = (PresentationOption.Alert | PresentationOption.Sound | PresentationOption.Badge);
notify.CategoryIdentifier = "category_a";
notify.ThreadIdentifier = "thread1";
notify.Trigger = tt;
}
public override void GenerateCalendarNotify(NotifyInfo info)
{
base.GenerateCalendarNotify(info);
//所有字段都是可选的,但您需要设置至少一个字段才能使触发器起作用。
//例如,如果您只设置小时和分钟字段,系统会在下一个指定的小时和分钟自动触发通知。
var tt = new iOSNotificationCalendarTrigger();
if(info.Day > 0)
tt.Day = info.Day;
if(info.Hour > 0)
tt.Hour = info.Hour;
if(info.Minute > 0)
tt.Minute = info.Minute;
if (info.Second > 0)
tt.Second = info.Second;
tt.Repeats = info.Repeats;
var notify = new iOSNotification();
notify.Title = info.Title;
notify.Body = info.Text;
notify.Badge = NotifyNumber++;
notify.ShowInForeground = true;
notify.ForegroundPresentationOption = (PresentationOption.Alert | PresentationOption.Sound | PresentationOption.Badge);
notify.CategoryIdentifier = "category_a";
notify.ThreadIdentifier = "thread1";
notify.Trigger = tt;
iOSNotificationCenter.ScheduleNotification(notify);
}
public override void ClearAllDisplayed()
{
base.ClearAllDisplayed();
iOSNotificationCenter.RemoveAllDeliveredNotifications();
}
}
}
#endif
#endif
五、通知管理器实现
至此,我们已经针对平台实现了本地通知推送功能,但对于接口使用者来说,什么时候什么情况下该使用什么平台的接口,使用者还需要进行各种条件的判断,显而对使用者友好度不高。所以,接下来我们还需要定义一个通知管理器,把这些逻辑差异性在内部处理掉。
代码如下:
#if(NotifyLocal)
using System;
using System.Collections;
using System.Collections.Generic;
#if UNITY_ANDROID
using Unity.Notifications.Android;
#elif UNITY_IOS
using Unity.Notifications.iOS;
#endif
using UnityEngine;
namespace Gamelogic
{
public class NotifyMono : MonoBehaviour
{
private void OnApplicationFocus(bool focus)
{
if(focus)
{
NotifyMgr.Notifyer.ClearAllDisplayed();
}
}
}
public class NotifyMgr
{
public static NotifyGenerater Notifyer;
private static GameObject _notifyGo;
private static NotifyMono _notifyMono;
private static bool _init = false;
public static void Init()
{
if (_init)
return;
if(_notifyMono == null)
{
_notifyGo = new GameObject("NotifyMono");
_notifyGo.transform.position = Vector3.zero;
_notifyMono = _notifyGo.AddComponent<NotifyMono>();
GameObject.DontDestroyOnLoad(_notifyGo);
}
_notifyMono.StartCoroutine(RequestNotifyPermission());
}
static IEnumerator RequestNotifyPermission()
{
#if UNITY_ANDROID
var request = new PermissionRequest();
while (request.Status == PermissionStatus.RequestPending)
yield return null;
Notifyer = new AndroidNotifyGenerater();
_init = true;
#elif UNITY_IOS
var aho = AuthorizationOption.Alert | AuthorizationOption.Badge;
using (var req = new AuthorizationRequest(aho, true))
{
while (!req.IsFinished)
{
yield return null;
};
string res = " RequestAuthorization:";
res += " finished: " + req.IsFinished;
res += " granted : " + req.Granted;
res += " error: " + req.Error;
res += " deviceToken: " + req.DeviceToken;
Debug.Log($"==============={res}");
Notifyer = new IosNotifyGenerater();
_init = true;
}
#endif
}
public static void GenerateIntervalNotify(NotifyInfo info)
{
if (Notifyer != null)
Notifyer.GenerateIntervalNotify(info);
}
public static void GenerateCalendarNotify(NotifyInfo info)
{
if (Notifyer != null)
Notifyer.GenerateCalendarNotify(info);
}
}
}
#endif
通过代码我们可以看到,使用者只要打开 [NotifyLocal] 宏,就可以把本地通知推送功编译进游戏里,否则就没有推送功能,就像没有添加任何东西一样。如需要间隔时间触发通知,就只要调用[GenerateIntervalNotify]方法; 如需要重复指定时间点触发通知,就只要调用[GenerateCalendarNotify]方法,其它的平台差异性和复杂性都很好的屏蔽在方法内了。
六、使用方法
#if (NotifyLocal)
NotifyMgr.Init();
var ninfo = new NotifyInfo();
ninfo.Day = 0;
ninfo.Hour = 1;
ninfo.Minute = 0;
ninfo.Second = 0;
ninfo.Title = "This is my game";
ninfo.Text = "Welcome to my game, hople play happy!";
NotifyMgr.GenerateIntervalNotify(ninfo);
#endif
如上面代码,打开游戏后,每隔一小时会触发一次通知推送,即使把游戏关闭也仍然有效。
七、效果测试
感谢阅读。