如何在Xamarin中使用Firebase和C#后端实现推送通知和苹果推送通知

4

在我从原生Android和iOS迁移到Xamarin.Forms的过程中,我决定使用AppCenter Push来实现通知功能,因为它是免费且易于使用的(当然,由于它是相对较新的东西,在线上指导较少,所以我花了很多时间让它正常工作)。您可以在如何实现AppCenter Push API?上找到我的原始分享。

直到微软宣布停用AppCenter Push并鼓励用户转向付费服务Azure后(详情请参见https://devblogs.microsoft.com/appcenter/app-center-mbaas-retirement/),我一直很满意这个工具。于是我决定回归使用原生的FCM和APN来实现推送通知。

问题在于没有直接的教程来完成整个事情。有一些零散的问题和解决方案,例如iOS .P8只适用于HTTP/2,而我的项目正在运行.NET Framework,不支持该协议。只有.NET Core才支持HTTP/2协议。


虽然回答自己的问题是可以的,但请考虑重新表述您的“问题”,使其不像一篇博客文章。除了标题之外,我不确定您是否真正在这里提出问题。 - user585968
2个回答

4

我的当前项目使用ASP.NET C#作为后端,使用Xamarin.Forms将通知发送到Xamarin.Android和Xamarin.iOS。如果您和我一样,请查看下面的答案,我在下面分享完全可用的C#后端和Xamarin.Forms解决方案。这样更多的用户可以从免费服务中受益,而不是被推向付费Azure服务。

第1部分 C# 后端 - C# ASP.NET 后端将分为2个部分,FCM和APNs。

1.1) Firebase(FCM)

  1. To setup FCM, you'll need to register an account. There are tonnes of guideline online, this is one of the good one https://xmonkeys360.com/2019/12/08/xamarin-forms-fcm-setup-configuration-part-i/. Remember to get the Server Key and download the google-services.json file to your Xamarin.Android project. Right-click and set the build action to "GoogleServiceJson" (Where can i add google-services.json in xamarin app).

  2. Below is my 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 (APNs)

  1. 在处理APNs时,有两种方法。一种是使用.P12常规方式,另一种是使用.P8的Apple身份验证密钥。我更喜欢使用.P8,因为.P12证书每年过期并需要更新。使用.P8的问题在于它使用的是不支持.Net Framework的HTTP/2协议,但幸运的是,我成功克服了这个问题。请参考如何在C#中实现基于令牌的苹果推送通知(使用p8文件)?,找到我在该帖子上的答案,了解如何为.Net Framework实现整个操作。*如果您已经在使用.Net Core,则答案与我的类似,但是无需使用自定义WinHTTPHandler。只需使用普通的HTTPClient即可。

第二部分Xamarin.Forms - 接下来,您需要支持在Xamarin.Forms项目中进行通知。

  1. For this part, it's not very difficult at all. All you have to do is refer to https://github.com/CrossGeeks/PushNotificationPlugin, download the Nuget and follow the instructions in the link to setup for your Xamarin projects (Forms, Android, iOS).

  2. The only thing that I wish to highlight is how & where to get your device token. Initially I was trying to get the device token at the code below (OnTokenRefresh). But you'll soon to notice that this code doesn't always get called, I suspect it will only get called once the token is refreshed, and not every time when you debug. In order to obtain your Device Token every time, just call CrossPushNotification.Current.Token anywhere in your project. Register that device token to your server backend. And use the device token to send the notification using my code at PART 1 above.

    CrossPushNotification.Current.OnTokenRefresh += (s,p) =>
    {
        System.Diagnostics.Debug.WriteLine($"TOKEN : {p.Token}");
    };
    

就是这样了!这很容易,但在将它们组合在一起之前,我花了几周时间进行尝试和错误。希望能为他人节省宝贵的时间。


一个非常详细的回答,但请考虑重新措辞您的问题,使其不像一篇博客。 - user585968
对于已过时的JavaScriptSerializer,请使用newtonsoft: string json = JsonConvert.SerializeObject(jObject); - Diego Venâncio
序列化器甚至没有被使用吗? - Bedir
只需使用Firebase SDK即可。手动编写POST请求已经过时了。 - Zun

-1

服务器:

尝试使用FirebaseAdmin进行服务器端编程。使用此软件包非常简单。

https://github.com/firebase/firebase-admin-dotnet

请按照以下设置说明进行操作:

https://firebase.google.com/docs/admin/setup#c

针对Xamarin应用程序:

我决定不使用CrossGeeks插件,这很简单明了。

针对Android:

安装相关的Xamarin.Firebase包,并在Android项目中创建自己的Firebase Messaging类,继承包FirebaseMessagingService。

[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>

2021年,这已经过时了。 - user1034912
你好,能否再详细说明一些? - ledragon
您不再需要使用<receiver>,而且'com.google.firebase.iid.FirebaseInstanceIdReceiver'已被FirebaseMessagingService所取代。 - user1034912
啊,好的,谢谢。那么只需要从清单中删除标签吗?我需要用什么替换吗?以上内容来自这里:https://learn.microsoft.com/en-us/xamarin/android/data-cloud/google-messaging/remote-notifications-with-fcm?tabs=windows - ledragon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接