官网给了本地通知的demo,但是没有点击事件,网上可以搜到安卓的点击跳转
但是没有ios的,我在官方给的demo基础上做了一些修改,实现了XF点击通知跳转
先上代码,再说原理
首先把官网的代码都用上
- pcl中
NotificationEventArgs.cs加了条属性ApplicationID ,用来指定跳转页面的参数
public class NotificationEventArgs : EventArgs
{
public string Title { get; set; }
public string Message { get; set; }
public string ApplicationID { get; set; }
}
INotificationManager.cs因为NotificationEventArgs.cs加了属性,所以这里调用的时候也要添加参数
public interface INotificationManager
{
event EventHandler NotificationReceived;
void Initialize();
int ScheduleNotification(string title, string message,string appid);
void ReceiveNotification(string title, string message, string appid);
}
重点:App类,新增一个构造函数用来跳转
public App(Page p)
{
InitializeComponent();
DependencyService.Get<INotificationManager>().Initialize();
MainPage = p;
}
生成通知的方法,我重新包了个类,不过代码大抵相同
public class NotificationService
{
/// <summary>
/// 弹出通知
/// </summary>
/// <param name="title"></param>
/// <param name="message"></param>
public static void Alert(string title, string message)
{
try
{
INotificationManager notificationManager = DependencyService.Get<INotificationManager>();
notificationManager.NotificationReceived += (sender, eventArgs) =>
{
var evtData = (NotificationEventArgs)eventArgs;
ShowNotification(evtData);
};
notificationManager.ScheduleNotification(title, message,"12345821"); // 参数12345821根据自己需求来
}
catch (Exception)
{
}
}
private static void ShowNotification(NotificationEventArgs ne)
{
//Device.BeginInvokeOnMainThread(() =>
//{
// var msg = new Label()
// {
// Text = $"Notification Received:\nTitle: {ne.Title}\nMessage: {ne.Message}"
// };
//});
}
}
- 安卓中
AndroidNotificationManager.cs
[assembly: Dependency(typeof(命名空间.AndroidNotificationManager))]
namespace 命名空间
{
public class AndroidNotificationManager : INotificationManager
{
#region 变量
const string channelId = "default";
const string channelName = "Default";
const string channelDescription = "The default channel for notifications.";
const int pendingIntentId = 0;
public const string TitleKey = "title";
public const string MessageKey = "通知事件的MessageKey";
bool channelInitialized = false;
int messageId = -1;
NotificationManager manager;
#endregion
#region 接口
public event EventHandler NotificationReceived;
public void Initialize()
{
CreateNotificationChannel();
}
public int ScheduleNotification(string title, string message, string appid)
{
if (!channelInitialized)
{
CreateNotificationChannel();
}
messageId++;
Intent intent = new Intent(AndroidApp.Context, typeof(MainActivity));
intent.PutExtra(TitleKey, title);
intent.PutExtra(MessageKey, message); // 在MainActivity会检查此MessageKey是否存在
intent.SetAction("通知事件");
PendingIntent pendingIntent = PendingIntent.GetActivity(AndroidApp.Context, pendingIntentId, intent, PendingIntentFlags.OneShot);
NotificationCompat.Builder builder = new NotificationCompat.Builder(AndroidApp.Context, channelId)
.SetContentIntent(pendingIntent)
.SetContentTitle(title)
.SetContentText(message)
.SetLargeIcon(BitmapFactory.DecodeResource(AndroidApp.Context.Resources, Resource.Drawable.approve_selected))
.SetSmallIcon(Resource.Drawable.approve_selected)
.SetDefaults((int)NotificationDefaults.Sound | (int)NotificationDefaults.Vibrate);
Notification notification = builder.Build();
manager.Notify(messageId, notification);
notification.Dispose();
return messageId;
}
public void ReceiveNotification(string title, string message, string appid)
{
var args = new NotificationEventArgs()
{
Title = title,
Message = message,
ApplicationID = appid,
};
NotificationReceived?.Invoke(null, args);
}
#endregion
#region 其他
void CreateNotificationChannel()
{
manager = (NotificationManager)AndroidApp.Context.GetSystemService(AndroidApp.NotificationService);
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
var channelNameJava = new Java.Lang.String(channelName);
var channel = new NotificationChannel(channelId, channelNameJava, NotificationImportance.Default)
{
Description = channelDescription
};
manager.CreateNotificationChannel(channel);
}
channelInitialized = true;
// manager.Wait
}
#endregion
}
}
MainActivity中
LaunchMode = LaunchMode.SingleTop这个一定加上
[Activity(Label = "name", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
MyInit();
CrossCurrentActivity.Current.Init(this, savedInstanceState);
MobileBarcodeScanner.Initialize(Application);
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
CreateNotificationFromIntent(Intent);
}
private void MyInit()
{
UserDialogs.Init(this);
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
var c = PermissionsImplementation.Current;
c.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
protected override void OnNewIntent(Intent intent)
{
base.OnNewIntent(intent);
CreateNotificationFromIntent(intent);
}
/// <summary>
/// 通知点击事件
/// </summary>
/// <param name="intent"></param>
void CreateNotificationFromIntent(Intent intent)
{
if (intent.Action == "通知事件" && intent.HasExtra("通知事件的MessageKey"))
{
LoadApplication(new App(new Views.MyPage()));
string title = intent.Extras.GetString(AndroidNotificationManager.TitleKey);
string message = intent.Extras.GetString(AndroidNotificationManager.MessageKey);
DependencyService.Get<INotificationManager>().ReceiveNotification(title, message);
}
}
}
跳转的关键就是在CreateNotificationFromIntent(Intent intent)中加一个LoadApplication(new App(new Views.MyPage()));
3. ios中
iOSNotificationManager.cs
[assembly: Dependency(typeof(命名空间.iOSNotificationManager))]
namespace 命名空间
{
public class iOSNotificationManager : INotificationManager
{
int messageId = -1;
bool hasNotificationsPermission;
public event EventHandler NotificationReceived;
public void Initialize()
{
// request the permission to use local notifications
UNUserNotificationCenter.Current.RequestAuthorization(UNAuthorizationOptions.Alert, (approved, err) =>
{
hasNotificationsPermission = approved;
});
}
public int ScheduleNotification(string title, string message, string appid)
{
// EARLY OUT: app doesn't have permissions
if (!hasNotificationsPermission)
{
return -1;
}
messageId++;
var content = new UNMutableNotificationContent()
{
Title = title,
Subtitle = appid,
Body = message,
Badge = 1
};
// Local notifications can be time or location based
// Create a time-based trigger, interval is in seconds and must be greater than 0
var trigger = UNTimeIntervalNotificationTrigger.CreateTrigger(0.25, false);
var request = UNNotificationRequest.FromIdentifier(messageId.ToString(), content, trigger);
UNUserNotificationCenter.Current.AddNotificationRequest(request, (err) =>
{
if (err != null)
{
throw new Exception($"通知失败: {err}");
}
});
return messageId;
}
public void ReceiveNotification(string title, string message, string appid)
{
var args = new NotificationEventArgs()
{
Title = title,
Message = message,
ApplicationID = appid
};
NotificationReceived?.Invoke(null, args);
}
}
}
iOSNotificationReceiver.cs
public class iOSNotificationReceiver : UNUserNotificationCenterDelegate
{
AppDelegate _app;
string id = "";
public iOSNotificationReceiver(AppDelegate a)
{
_app = a;
}
public override void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
{
DependencyService.Get<INotificationManager>().ReceiveNotification(notification.Request.Content.Title, notification.Request.Content.Body, notification.Request.Content.Subtitle);
id = notification.Request.Content.Subtitle;
// alerts are always shown for demonstration but this can be set to "None"
// to avoid showing alerts if the app is in the foreground
completionHandler(UNNotificationPresentationOptions.Alert);
}
public override void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler)
{
_app.LoadApp(id);
// base.DidReceiveNotificationResponse(center, response, completionHandler);
}
}
AppDelegate.cs
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
UNUserNotificationCenter.Current.Delegate = new iOSNotificationReceiver(this);
LoadApplication(new App());
return base.FinishedLaunching(app, options);
}
public void LoadApp(string appid)
{
Xamarin.Forms.Application.Current.MainPage.Navigation.PushAsync(new Page(), false);
}
}
原理:Xamarin.form官方是没有集成通知功能的,而且安卓和ios的实现也不一样,估计因为ios的权限管理太严格,搞通知的话需要手动配置的地方太多。所以官方推荐上也是在pcl上写接口,然后native实现。
native实现:安卓有自己的通知方式,直接按照官方demo实现本地通知接口即可,主要问题是点击通知跳转。这次实现点击功能发现了xamarin.form和native的一个区别,native的跳转是通过Activity实现的,不同的页面对应不同的Activity,而xf只有一个Activity,就是MainActivity,虽然有的例子在跳转这新写了一个。
我的方法在ios的实现原理和安卓一样,都是重新加载一下整个app,然后在加载时跳转到想进的页,不过ios注意一下权限问题,我还根据官方demo改了下Info.plist,不知道不改会不会出错,反正我改了。