在我从原生Android和iOS迁移到Xamarin.Forms的过程中,我决定使用AppCenter Push进行通知,因为它是免费且易于使用的(当然,我花了大量时间使其发挥作用,因为它相对较新,在线指导较少(。您可以在"如何实现AppCenterPushneneneba API?"中找到我的原始分享?。
我很高兴,直到微软宣布它将退出AppCenter推送(https://devblogs.microsoft.com/appcenter/app-center-mbaas-retirement/)并鼓励用户转移到Azure(这是一项付费服务(。我决定重新使用本地FCM和APN来进行推送通知。
问题是没有关于如何完成整件事的直接教程。存在问题&这里和那里的解决方案,如iOS.P8只在HTTP/2上工作,而我的项目运行在不支持的.Net Framework上。只有.Net Core可以使用HTTP/2协议。
我当前的项目运行在ASP.NET C#作为后端,它使用Xamarin.Forms向Xamarin.Android和Xamarin.iOS发送通知。如果你和我一样,请在下面找到我的答案,我将在下面分享完全可用的C#后端和Xamaring.Forms解决方案。这样更多的用户可以从免费服务中受益,而不是被推到Azure付费服务。
第1部分C#后端-C#ASP.NET后端。它将分为两部分,分别用于FCM和APN。
1.1(Firebase(FCM(
-
要设置FCM,您需要注册一个帐户。网上有很多指南,这是一个很好的指南https://xmonkeys360.com/2019/12/08/xamarin-forms-fcm-setup-configuration-part-i/.请记住获取服务器密钥并将谷歌服务.json文件下载到您的Xamarin.Android项目中。右键单击并将构建操作设置为">谷歌服务Json";(我在哪里可以在xamarin应用程序中添加google-services.json(。
-
下面是我的Firebase
using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Web.Script.Serialization; namespace PushNotificationLibrary { public class FirebaseCloudMessagingPush { private const string WEB_ADDRESS = "https://fcm.googleapis.com/fcm/send"; private const string SENDER_ID = "YOUR SENDER ID"; private const string SERVER_KEY = "YOUR SERVER KEY"; public string SendNotification(string deviceToken, string title, string message, string priority = "high", int badge = 0, List<Tuple<string, string>> parameters = null) { var result = "-1"; var httpWebRequest = (HttpWebRequest)WebRequest.Create(WEB_ADDRESS); parameters = parameters ?? new List<Tuple<string, string>>(); httpWebRequest.ContentType = "application/json"; httpWebRequest.Headers.Add(string.Format("Authorization: key={0}", SERVER_KEY)); httpWebRequest.Headers.Add(string.Format("Sender: id={0}", SENDER_ID)); httpWebRequest.Method = "POST"; if (title.Length > 100) title = title.Substring(0, 95) + "..."; //Message cannot exceed 100 if (message.Length > 100) message = message.Substring(0, 95) + "..."; JObject jObject = new JObject(); jObject.Add("to", deviceToken); jObject.Add("priority", priority); jObject.Add("content_available", true); JObject jObjNotification = new JObject(); jObjNotification.Add("body", message); jObjNotification.Add("title", title); jObject.Add("notification", jObjNotification); JObject jObjData = new JObject(); jObjData.Add("badge", badge); jObjData.Add("body", message); jObjData.Add("title", title); foreach (Tuple<string, string> parameter in parameters) { jObjData.Add(parameter.Item1, parameter.Item2); } jObject.Add("data", jObjData); var serializer = new JavaScriptSerializer(); using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream())) { string json = jObject.ToString(); streamWriter.Write(json); streamWriter.Flush(); } var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse(); using (var streamReader = new StreamReader(httpResponse.GetResponseStream())) { result = streamReader.ReadToEnd(); } return result; } } }
1.2(iOS(APN(
- 对于APN,有两种做事方式。一种是使用.P12常规方式。另一种方法是使用.P8使用Apple Auth密钥。我更喜欢使用.P8,因为.P12证书每年到期,需要每年更新。使用.P8的问题是,它使用的是HTTP/2,这在.Net Framework中不受支持,但谢天谢地,我成功地克服了这一点。请参阅如何在C#中实现基于apple令牌的推送通知(使用p8文件(?,请在关于如何为.Net Framework实现整个功能的帖子中找到我的答案*如果您已经在使用.Net Core,答案将与我的类似,但您不必使用自定义的WinHTTPHandler。正常的HTTPClient就可以了
第2部分Xamarin.Forms-接下来,您必须在Xamarin.Form项目中支持通知。
-
对于这个部分来说,一点也不难。你所要做的就是参考https://github.com/CrossGeeks/PushNotificationPlugin,下载Nuget并按照链接中的说明为您的Xamarin项目(Forms、Android、iOS(进行设置。
-
我唯一想强调的是;在哪里获取您的设备令牌。最初,我试图在下面的代码中获取设备令牌(OnTokenRefresh(。但您很快就会注意到,这段代码并不总是被调用,我怀疑它只会在令牌刷新后被调用,而不是每次调试时都被调用。为了每次都能获得您的设备令牌,只需在项目中的任何位置调用
CrossPushNotification.Current.Token
即可。将该设备令牌注册到服务器后端。并使用我在上面第1部分的代码使用设备令牌发送通知。CrossPushNotification.Current.OnTokenRefresh += (s,p) => { System.Diagnostics.Debug.WriteLine($"TOKEN : {p.Token}"); };
就是这样!这很容易,但在把它们拼凑起来之前,我花了几个星期的时间反复尝试。希望它能节省别人宝贵的时间。
服务器:
请尝试服务器端的FirebaseAdmin。这个包裹真是太棒了。
https://github.com/firebase/firebase-admin-dotnet
遵循以下设置说明:
https://firebase.google.com/docs/admin/setup#c
对于Xamarin应用程序:
我决定不想使用CrossGeeks插件,它非常简单。
对于Android:
安装相关的Xamarin.Firebase包,并在继承包FirebaseMessagingService的Android项目中创建自己的Firebase消息类。
[Service]
[IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
class PushNotificationFirebaseMessagingService : FirebaseMessagingService
{
private static string foregroundChannelId = "9001";
public override void OnNewToken(string refreshedToken)
{
base.OnNewToken(refreshedToken);
SendRegistrationToServer(refreshedToken);
}
private void SendRegistrationToServer(string token)
{
//Your code here to register device token on server
}
public override void OnMessageReceived(RemoteMessage message)
{
SendNotification(message);
base.OnMessageReceived(message);
}
private void SendNotification(RemoteMessage message)
{
try
{
var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
var notificationChannel = new NotificationChannel(foregroundChannelId, "messaging_channel", NotificationImportance.High);
var audioAttributes = new AudioAttributes.Builder()
.SetContentType(AudioContentType.Sonification)
.SetUsage(AudioUsageKind.Notification).Build();
var notificationUri = RingtoneManager.GetDefaultUri(RingtoneType.Notification);
notificationChannel.EnableLights(true);
notificationChannel.EnableVibration(true);
notificationChannel.SetSound(notificationUri, audioAttributes);
notificationManager.CreateNotificationChannel(notificationChannel);
var remoteNotification = message.GetNotification();
var builder = new Notification.Builder(this, foregroundChannelId)
.SetContentTitle(remoteNotification.Title)
.SetContentText(remoteNotification.Body)
.SetSmallIcon(Resource.Mipmap.icon);
var notification = builder.Build();
notificationManager.Notify(0, notification);
}
catch (Exception ex)
{
}
}
}
将以下内容添加到Application标记中的AndroidManifest.xml中。
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver" android:exported="false" />
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="${applicationId}" />
</intent-filter>
</receiver>