Dela via


Självstudie: Skicka push-meddelanden till specifika Android-appar med Hjälp av Azure Notification Hubs

Kommentar

Information om utfasning och migrering av Firebase Cloud Messaging finns i Google Firebase Cloud Messaging-migrering.

Denna självstudie visar hur du använder Azure Notification Hubs till att skicka push-meddelanden till en specifik appanvändare på en specifik enhet. En ASP.NET WebAPI-serverdel används för att autentisera klienter och generera meddelanden, vilket visas i artikeln Registrering från din apps serverdel. Den här självstudien bygger på meddelandehubben som du skapade i Självstudie: Push-meddelanden till Android-enheter med hjälp av Azure Notification Hubs och Firebase Cloud Messaging.

I den här självstudien gör du följande:

  • Skapa ett WebAPI-projekt för serverdelen som autentiserar användare.
  • Uppdatera Android-programmet.
  • Testa appen

Förutsättningar

Slutför självstudien : Skicka push-meddelanden till Android-enheter med hjälp av Azure Notification Hubs och Firebase Cloud Messaging innan du gör den här självstudien.

Skapa ett WebAPI-projekt

I följande avsnitt beskrivs skapandet av en ny ASP.NET-WebAPI-serverdel. Den här processen har tre huvudsakliga syften:

  • Autentisera klienter: Du lägger till en meddelandehanterare för att autentisera klientbegäranden och associera användaren med begäran.
  • Registrera dig för meddelanden med hjälp av WebAPI-serverdel: Du lägger till en domänkontrollant för att hantera nya registreringar för en klientenhet för att ta emot meddelanden. Det autentiserade användarnamnet läggs automatiskt till i registreringen som en tagg.
  • Skicka meddelanden till klienter: Du lägger till en kontrollant som gör att användare kan utlösa en säker push-överföring till enheter och klienter som är associerade med taggen.

Skapa den nya ASP.NET Core 6.0-webb-API-serverdelen genom att utföra följande åtgärder:

Börja med att starta Visual Studio. På menyn Verktyg väljer du Tillägg och uppdateringar. Sök efter NuGet Package Manager i din version av Visual Studio och kontrollera att du har den senaste versionen. Om din version inte är den senaste versionen, avinstallera den och installera sedan om NuGet Package Manager.

Screenshot of the Extensions and Updates dialog box with the NuGet Package manage for Visual Studios package highlighted.

Kommentar

Kontrollera att du har installerat Azure SDK för Visual Studio för webbplatsdistribution.

  1. Starta Visual Studio eller Visual Studio Express.

  2. Välj Server Explorer och logga in på ditt Azure-konto. För att skapa webbplatsresurser på ditt konto, måste du vara inloggad.

  3. I Menyn Arkiv i Visual Studio väljer du Nytt>projekt.

  4. Ange webb-API i sökrutan.

  5. Välj projektmallen ASP.NET Core Web API och välj Nästa.

  6. I dialogrutan Konfigurera ditt nya projekt namnger du projektet AppBackend och väljer Nästa.

  7. I dialogrutan Ytterligare information :

    • Bekräfta att Ramverket är .NET 6.0 (långsiktigt stöd).
    • Bekräfta att kryssrutan för Använd kontrollanter (avmarkera om du vill använda minimala API:er) är markerad.
    • Avmarkera Aktivera OpenAPI-stöd.
    • Välj Skapa.

Ta bort WeatherForecast-mallfilerna

  1. Ta bort exempelfilerna WeatherForecast.cs och Controllers/WeatherForecastController.cs från det nya AppBackend-projektet .
  2. Öppna Egenskaper\starta Inställningar.json.
  3. Ändra launchUrl-egenskaper från weatherforcast till appbackend.

I fönstret Konfigurera Microsoft Azure Web App väljer du en prenumeration och sedan i listan App Service plan gör du någon av följande åtgärder:

  • Välj en Azure App Service-plan som du redan har skapat.
  • Välj Skapa en ny app service-plan, och skapa sedan en.

Du behöver ingen databas för den här självstudiekursen. När du har valt app service-plan väljer du OK för att skapa projektet.

The Configure Microsoft Azure Web App window

Om du inte ser den här sidan för att konfigurera App Service-plan fortsätter du med självstudien. Du kan konfigurera den när du publicerar appen senare.

Autentisera klienter mot WebAPI-serverdelen

I det här avsnittet skapar du en ny meddelandehanterarklass med namnet AuthenticationTestHandler för den nya serverdelen. Den här klassen härleds från DelegatingHandler och läggs till som en meddelandehanterare så att den kan bearbeta alla begäranden som kommer till serverdelen.

  1. I Solution Explorer högerklickar du på projektet AppBackend och väljer sedan Lägg till och sedan Klass.

  2. Ge den nya klassen namnet AuthenticationTestHandler.cs och välj sedan Lägg till för att generera klassen. Den här klassen autentiserar användare med hjälp av Grundläggande autentisering för enkelhetens skull. Din app kan använda alla autentiseringsscheman.

  3. Lägg till följande using-instruktioner i AuthenticationTestHandler.cs:

    using System.Net.Http;
    using System.Threading;
    using System.Security.Principal;
    using System.Net;
    using System.Text;
    using System.Threading.Tasks;
    
  4. Ersätt AuthenticationTestHandler-klassdefinitionen i AuthenticationTestHandler.cs med följande kod:

    Hanteraren godkänner begäran om följande tre villkor är uppfyllda:

    • Begäran innehåller en auktoriseringsrubrik.
    • Begäran använder grundläggande autentisering.
    • Strängen för användarnamn och lösenordssträngen är samma sträng.

    I annat fall avvisas begäran. Den här begäran är inte en riktig autentiserings- och auktoriseringsmetod. Det är bara ett enkelt exempel för den här självstudien.

    Om meddelandebegäran autentiseras och godkänns av AuthenticationTestHandler, kopplas användaren med den grundläggande autentiseringen till den aktuella begäran i HttpContext. Senare används användarinformationen i HttpContext av en annan kontrollant (RegisterController) för att lägga till en tagg till meddelanderegistreringsbegäran.

    public class AuthenticationTestHandler : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var authorizationHeader = request.Headers.GetValues("Authorization").First();
    
            if (authorizationHeader != null && authorizationHeader
                .StartsWith("Basic ", StringComparison.InvariantCultureIgnoreCase))
            {
                string authorizationUserAndPwdBase64 =
                    authorizationHeader.Substring("Basic ".Length);
                string authorizationUserAndPwd = Encoding.Default
                    .GetString(Convert.FromBase64String(authorizationUserAndPwdBase64));
                string user = authorizationUserAndPwd.Split(':')[0];
                string password = authorizationUserAndPwd.Split(':')[1];
    
                if (VerifyUserAndPwd(user, password))
                {
                    // Attach the new principal object to the current HttpContext object
                    HttpContext.Current.User =
                        new GenericPrincipal(new GenericIdentity(user), new string[0]);
                    System.Threading.Thread.CurrentPrincipal =
                        System.Web.HttpContext.Current.User;
                }
                else return Unauthorized();
            }
            else return Unauthorized();
    
            return base.SendAsync(request, cancellationToken);
        }
    
        private bool VerifyUserAndPwd(string user, string password)
        {
            // This is not a real authentication scheme.
            return user == password;
        }
    
        private Task<HttpResponseMessage> Unauthorized()
        {
            var response = new HttpResponseMessage(HttpStatusCode.Forbidden);
            var tsc = new TaskCompletionSource<HttpResponseMessage>();
            tsc.SetResult(response);
            return tsc.Task;
        }
    }
    

    Kommentar

    Säkerhetsmeddelande: Klassen AuthenticationTestHandler tillhandahåller inte riktig autentisering. Den används endast för att efterlikna grundläggande autentisering och är inte säker. Du måste implementera en säker autentiseringsmekanism i dina tjänster och program i produktionsmiljön.

  5. Om du vill registrera meddelandehanteraren lägger du till följande kod i slutet av Register metoden i filen Program.cs :

    config.MessageHandlers.Add(new AuthenticationTestHandler());
    
  6. Spara dina ändringar.

Registrera för meddelanden med hjälp av WebAPI-serverdelen

I det här avsnittet lägger du till en ny kontrollant till WebAPI-serverdelen som ska hantera begäranden för att registrera en användare och en enhet för meddelanden med hjälp av klientbiblioteket för meddelandehubbar. Kontrollanten lägger till en användartagg för den användare som autentiserades och kopplades till HttpContext av AuthenticationTestHandler. Taggen har strängformatet "username:<actual username>".

  1. Högerklicka på AppBackend-projektet i Solution Explorer och välj sedan Hantera NuGet-paket.

  2. I den vänstra rutan välj Online och sedan, i rutan Sök skriver du Microsoft.Azure.NotificationHubs.

  3. Välj Microsoft Azure Notification Hubs i resultatlistan och sedan Installera. Slutför installationen och stäng sedan fönstret för NuGet-pakethanteraren.

    Den här åtgärden lägger till en referens i Azure Notification Hubs SDK med hjälp av Microsoft.Azure.Notification Hubs-NuGet-paketet.

  4. Skapa en ny klassfil som representerar anslutningen med meddelandehubben som används för att skicka meddelanden. I Solution Explorer högerklickar du på mappen Modeller, välj Lägg till och sedan Klass. Ge den nya klassen namnet Notifications.cs och välj sedan Lägg till för att generera den nya klassen.

    The Add New Item window

  5. Lägg till följande using-instruktion överst i Notifications.cs-filen:

    using Microsoft.Azure.NotificationHubs;
    
  6. Ersätt Notifications-klassdefinitionen med följande kod och ersätt de två platshållarna med anslutningssträngen (med fullständig åtkomst) för din meddelandehubb samt hubbnamnet (tillgängligt på Azure Portal):

    public class Notifications
    {
        public static Notifications Instance = new Notifications();
    
        public NotificationHubClient Hub { get; set; }
    
        private Notifications() {
            Hub = NotificationHubClient.CreateClientFromConnectionString("<your hub's DefaultFullSharedAccessSignature>",
                                                                            "<hub name>");
        }
    }
    

    Viktigt!

    Ange namnet och DefaultFullSharedAccessSignature för din hubb innan du fortsätter.

  7. Skapa nu en ny kontrollant med namnet RegisterController. I Solution Explorer högerklickar du på mappen Styrenheter. Välj sedan Lägg till och sedan Styrenhet.

  8. Välj API Controller – Tom och välj sedan Lägg till.

  9. I rutan Kontrollnamn skriver du RegisterController som namn på den nya klassen och väljer sedan Lägg till.

    The Add Controller window.

  10. Lägg till följande using-instruktioner i RegisterController.cs:

    using Microsoft.Azure.NotificationHubs;
    using Microsoft.Azure.NotificationHubs.Messaging;
    using AppBackend.Models;
    using System.Threading.Tasks;
    using System.Web;
    
  11. Lägg till följande kod i RegisterController-klassdefinitionen. Lägg till en användartagg för den användare som är kopplad till HttpContext i den här koden. Användaren autentiserades och kopplades till HttpContext med hjälp av meddelandefiltret som du la till, AuthenticationTestHandler. Du kan också lägga till valfria kontroller som kontrollerar att användaren har behörighet att registrera sig för de begärda taggarna.

    private NotificationHubClient hub;
    
    public RegisterController()
    {
        hub = Notifications.Instance.Hub;
    }
    
    public class DeviceRegistration
    {
        public string Platform { get; set; }
        public string Handle { get; set; }
        public string[] Tags { get; set; }
    }
    
    // POST api/register
    // This creates a registration id
    public async Task<string> Post(string handle = null)
    {
        string newRegistrationId = null;
    
        // make sure there are no existing registrations for this push handle (used for iOS and Android)
        if (handle != null)
        {
            var registrations = await hub.GetRegistrationsByChannelAsync(handle, 100);
    
            foreach (RegistrationDescription registration in registrations)
            {
                if (newRegistrationId == null)
                {
                    newRegistrationId = registration.RegistrationId;
                }
                else
                {
                    await hub.DeleteRegistrationAsync(registration);
                }
            }
        }
    
        if (newRegistrationId == null) 
            newRegistrationId = await hub.CreateRegistrationIdAsync();
    
        return newRegistrationId;
    }
    
    // PUT api/register/5
    // This creates or updates a registration (with provided channelURI) at the specified id
    public async Task<HttpResponseMessage> Put(string id, DeviceRegistration deviceUpdate)
    {
        RegistrationDescription registration = null;
        switch (deviceUpdate.Platform)
        {
            case "mpns":
                registration = new MpnsRegistrationDescription(deviceUpdate.Handle);
                break;
            case "wns":
                registration = new WindowsRegistrationDescription(deviceUpdate.Handle);
                break;
            case "apns":
                registration = new AppleRegistrationDescription(deviceUpdate.Handle);
                break;
            case "fcm":
                registration = new FcmRegistrationDescription(deviceUpdate.Handle);
                break;
            default:
                throw new HttpResponseException(HttpStatusCode.BadRequest);
        }
    
        registration.RegistrationId = id;
        var username = HttpContext.Current.User.Identity.Name;
    
        // add check if user is allowed to add these tags
        registration.Tags = new HashSet<string>(deviceUpdate.Tags);
        registration.Tags.Add("username:" + username);
    
        try
        {
            await hub.CreateOrUpdateRegistrationAsync(registration);
        }
        catch (MessagingException e)
        {
            ReturnGoneIfHubResponseIsGone(e);
        }
    
        return Request.CreateResponse(HttpStatusCode.OK);
    }
    
    // DELETE api/register/5
    public async Task<HttpResponseMessage> Delete(string id)
    {
        await hub.DeleteRegistrationAsync(id);
        return Request.CreateResponse(HttpStatusCode.OK);
    }
    
    private static void ReturnGoneIfHubResponseIsGone(MessagingException e)
    {
        var webex = e.InnerException as WebException;
        if (webex.Status == WebExceptionStatus.ProtocolError)
        {
            var response = (HttpWebResponse)webex.Response;
            if (response.StatusCode == HttpStatusCode.Gone)
                throw new HttpRequestException(HttpStatusCode.Gone.ToString());
        }
    }
    
  12. Spara dina ändringar.

Skicka meddelanden från WebAPI-serverdelen

I det här avsnittet kan du lägga till en ny domänkontrollant som exponerar en metod för klientenheter för att skicka en avisering. Meddelandet baseras på taggen för användarnamnet som använder Azure Notification Hubs .NET-biblioteket i ASP.NET-WebAPI-serverdelen.

  1. Skapa en ny domänkontrollant med namnet NotificationsController på samma sätt som du skapade RegisterController i föregående avsnitt.

  2. Lägg till följande using-instruktioner i NotificationsController.cs:

    using AppBackend.Models;
    using System.Threading.Tasks;
    using System.Web;
    
  3. Lägg till följande metod i NotificationsController-klassen:

    Den här koden skickar en meddelandetyp som är baserad på PNS-parametern (Platform Notification Service) pns. Värdet för to_tag används för att ställa in usernamne-taggen för meddelandet. Den här taggen måste matcha en username-tagg för en aktiv meddelandehubbsregistrering. Meddelandet hämtas från innehållsdelen i POST-begäran och formateras för PNS-måltjänsten.

    Beroende på det PNS-system som dina stödda enheter använder för att ta emot meddelanden, stöds meddelanden i olika format. På Windows-enheter kan du till exempel använda ett popup-meddelande med WNS som inte stöds direkt av en annan PNS. I dessa fall måste din serverdel alltså formatera meddelandet till ett meddelande som stöds för PNS-tjänsten för enheter som du vill ha stöd för. Därefter använder du lämpligt överförings-API i NotificationHubClient-klassen.

    public async Task<HttpResponseMessage> Post(string pns, [FromBody]string message, string to_tag)
    {
        var user = HttpContext.Current.User.Identity.Name;
        string[] userTag = new string[2];
        userTag[0] = "username:" + to_tag;
        userTag[1] = "from:" + user;
    
        Microsoft.Azure.NotificationHubs.NotificationOutcome outcome = null;
        HttpStatusCode ret = HttpStatusCode.InternalServerError;
    
        switch (pns.ToLower())
        {
            case "wns":
                // Windows 8.1 / Windows Phone 8.1
                var toast = @"<toast><visual><binding template=""ToastText01""><text id=""1"">" + 
                            "From " + user + ": " + message + "</text></binding></visual></toast>";
                outcome = await Notifications.Instance.Hub.SendWindowsNativeNotificationAsync(toast, userTag);
                break;
            case "apns":
                // iOS
                var alert = "{\"aps\":{\"alert\":\"" + "From " + user + ": " + message + "\"}}";
                outcome = await Notifications.Instance.Hub.SendAppleNativeNotificationAsync(alert, userTag);
                break;
            case "fcm":
                // Android
                var notif = "{ \"data\" : {\"message\":\"" + "From " + user + ": " + message + "\"}}";
                outcome = await Notifications.Instance.Hub.SendFcmNativeNotificationAsync(notif, userTag);
                break;
        }
    
        if (outcome != null)
        {
            if (!((outcome.State == Microsoft.Azure.NotificationHubs.NotificationOutcomeState.Abandoned) ||
                (outcome.State == Microsoft.Azure.NotificationHubs.NotificationOutcomeState.Unknown)))
            {
                ret = HttpStatusCode.OK;
            }
        }
    
        return Request.CreateResponse(ret);
    }
    
  4. Tryck på F5 för att köra programmet och för att kontrollera att allt fungerar som det ska hittills. Appen öppnar en webbläsare och den visas på startsidan för ASP.NET.

Publicera den nya WebAPI-serverdelen

Därefter distribuerar du appen till en Azure-webbplats så att den är tillgänglig från alla enheter.

  1. Högerklicka på AppBackend-projektet och välj sedan Publicera.

  2. Välj Microsoft Azure App Service som publiceringsmål och välj sedan \*\*Publicera. Fönstret Skapa App Service öppnas. Här kan du skapa alla nödvändiga Azure-resurser för att köra ASP.NET-webbapp i Azure.

    The Microsoft Azure App Service tile

  3. Välj ditt Azure-konto i fönstret Skapa App Service. Välj Ändra typ>Webbapp. Behåll standardnamnet på webbappen och välj sedan Prenumeration, Resursgrupp och App Service Plan.

  4. Välj Skapa.

  5. Notera egenskapen Plats-URL i avsnittet Sammanfattning. Den här URL:en är din slutpunkt för serverdel senare under självstudien.

  6. Välj Publicera.

När du har slutfört guiden publiceras ASP.NET-webbappen till Azure och sedan öppnas appen i standardwebbläsaren. Programmet kan visas i Azure App Services.

URL:en använder webbappens namn som du angav tidigare, med formatet http://< app_name.azurewebsites.net>.

Skapa Android-projektet

Nästa steg är att uppdatera Android-programmet som skapades i Självstudie: Push-meddelanden till Android-enheter med hjälp av Azure Notification Hubs och Firebase Cloud Messaging.

  1. Öppna filen res/layout/activity_main.xml och ersätt följande innehållsdefinitioner:

    Nya EditText-kontroller läggs till för att kunna logga in som en användare. Ett fält har också lagts till för en användarnamntagg som ska ingå i meddelanden som du skickar:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    
    <EditText
        android:id="@+id/usernameText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="@string/usernameHint"
        android:layout_above="@+id/passwordText"
        android:layout_alignParentEnd="true" />
    <EditText
        android:id="@+id/passwordText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="@string/passwordHint"
        android:inputType="textPassword"
        android:layout_above="@+id/buttonLogin"
        android:layout_alignParentEnd="true" />
    <Button
        android:id="@+id/buttonLogin"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/loginButton"
        android:onClick="login"
        android:layout_above="@+id/toggleButtonFCM"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="24dp" />
    <ToggleButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textOn="WNS on"
        android:textOff="WNS off"
        android:id="@+id/toggleButtonWNS"
        android:layout_toLeftOf="@id/toggleButtonFCM"
        android:layout_centerVertical="true" />
    <ToggleButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textOn="FCM on"
        android:textOff="FCM off"
        android:id="@+id/toggleButtonFCM"
        android:checked="true"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true" />
    <ToggleButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textOn="APNS on"
        android:textOff="APNS off"
        android:id="@+id/toggleButtonAPNS"
        android:layout_toRightOf="@id/toggleButtonFCM"
        android:layout_centerVertical="true" />
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/editTextNotificationMessageTag"
        android:layout_below="@id/toggleButtonFCM"
        android:layout_centerHorizontal="true"
        android:hint="@string/notification_message_tag_hint" />
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/editTextNotificationMessage"
        android:layout_below="@+id/editTextNotificationMessageTag"
        android:layout_centerHorizontal="true"
        android:hint="@string/notification_message_hint" />
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/send_button"
        android:id="@+id/sendbutton"
        android:onClick="sendNotificationButtonOnClick"
        android:layout_below="@+id/editTextNotificationMessage"
        android:layout_centerHorizontal="true" />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        android:id="@+id/text_hello"
        />
    </RelativeLayout>
    
  2. Öppna filen res/values/strings.xml och ersätt definitionen send_button med följande rader som omdefinierar strängen för send_button, och lägg till strängar för de andra kontrollerna:

    <string name="usernameHint">Username</string>
    <string name="passwordHint">Password</string>
    <string name="loginButton">1. Sign in</string>
    <string name="send_button">2. Send Notification</string>
    <string name="notification_message_hint">Notification message</string>
    <string name="notification_message_tag_hint">Recipient username</string>
    

    Den grafiska layouten för main_activity.xml bör nu se ut som följande bild:

    Screenshot of an emulator displaying what the main activity X M L graphical layout will look like.

  3. Skapa en ny klass med namnet RegisterClient i samma paket som klassen MainActivity. Använd koden nedan för den nya klassfilen.

    
    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    import java.util.Set;
    
    import org.apache.http.HttpResponse;
    import org.apache.http.HttpStatus;
    import org.apache.http.client.ClientProtocolException;
    import org.apache.http.client.HttpClient;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.client.methods.HttpPut;
    import org.apache.http.client.methods.HttpUriRequest;
    import org.apache.http.entity.StringEntity;
    import org.apache.http.impl.client.DefaultHttpClient;
    import org.apache.http.util.EntityUtils;
    import org.json.JSONArray;
    import org.json.JSONException;
    import org.json.JSONObject;
    
    import android.content.Context;
    import android.content.SharedPreferences;
    import android.util.Log;
    
    public class RegisterClient {
        private static final String PREFS_NAME = "ANHSettings";
        private static final String REGID_SETTING_NAME = "ANHRegistrationId";
        private String Backend_Endpoint;
        SharedPreferences settings;
        protected HttpClient httpClient;
        private String authorizationHeader;
    
        public RegisterClient(Context context, String backendEndpoint) {
            super();
            this.settings = context.getSharedPreferences(PREFS_NAME, 0);
            httpClient =  new DefaultHttpClient();
            Backend_Endpoint = backendEndpoint + "/api/register";
        }
    
        public String getAuthorizationHeader() {
            return authorizationHeader;
        }
    
        public void setAuthorizationHeader(String authorizationHeader) {
            this.authorizationHeader = authorizationHeader;
        }
    
        public void register(String handle, Set<String> tags) throws ClientProtocolException, IOException, JSONException {
            String registrationId = retrieveRegistrationIdOrRequestNewOne(handle);
    
            JSONObject deviceInfo = new JSONObject();
            deviceInfo.put("Platform", "fcm");
            deviceInfo.put("Handle", handle);
            deviceInfo.put("Tags", new JSONArray(tags));
    
            int statusCode = upsertRegistration(registrationId, deviceInfo);
    
            if (statusCode == HttpStatus.SC_OK) {
                return;
            } else if (statusCode == HttpStatus.SC_GONE){
                settings.edit().remove(REGID_SETTING_NAME).commit();
                registrationId = retrieveRegistrationIdOrRequestNewOne(handle);
                statusCode = upsertRegistration(registrationId, deviceInfo);
                if (statusCode != HttpStatus.SC_OK) {
                    Log.e("RegisterClient", "Error upserting registration: " + statusCode);
                    throw new RuntimeException("Error upserting registration");
                }
            } else {
                Log.e("RegisterClient", "Error upserting registration: " + statusCode);
                throw new RuntimeException("Error upserting registration");
            }
        }
    
        private int upsertRegistration(String registrationId, JSONObject deviceInfo)
                throws UnsupportedEncodingException, IOException,
                ClientProtocolException {
            HttpPut request = new HttpPut(Backend_Endpoint+"/"+registrationId);
            request.setEntity(new StringEntity(deviceInfo.toString()));
            request.addHeader("Authorization", "Basic "+authorizationHeader);
            request.addHeader("Content-Type", "application/json");
            HttpResponse response = httpClient.execute(request);
            int statusCode = response.getStatusLine().getStatusCode();
            return statusCode;
        }
    
        private String retrieveRegistrationIdOrRequestNewOne(String handle) throws ClientProtocolException, IOException {
            if (settings.contains(REGID_SETTING_NAME))
                return settings.getString(REGID_SETTING_NAME, null);
    
            HttpUriRequest request = new HttpPost(Backend_Endpoint+"?handle="+handle);
            request.addHeader("Authorization", "Basic "+authorizationHeader);
            HttpResponse response = httpClient.execute(request);
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                Log.e("RegisterClient", "Error creating registrationId: " + response.getStatusLine().getStatusCode());
                throw new RuntimeException("Error creating Notification Hubs registrationId");
            }
            String registrationId = EntityUtils.toString(response.getEntity());
            registrationId = registrationId.substring(1, registrationId.length()-1);
    
            settings.edit().putString(REGID_SETTING_NAME, registrationId).commit();
    
            return registrationId;
        }
    }
    

    Den här komponenten implementerar DE REST-anrop som krävs för att kontakta appens serverdel för att registrera sig för push-meddelanden. Den lagrar även lokalt de registrationIds som skapas av den meddelandehubb som anges i Registrering från din apps serverdel. Den använder en auktoriseringstoken som lagras i lokal lagring när du klickar på knappen Logga in .

  4. I klassen MainActivity och lägg till ett fält för RegisterClient klassen och en sträng för ASP.NET serverdelens slutpunkt. Kom ihåg att ersätta <Enter Your Backend Endpoint> med din faktiska serverdelsslutpunkt som hämtades tidigare. Exempel: http://mybackend.azurewebsites.net

    private RegisterClient registerClient;
    private static final String BACKEND_ENDPOINT = "<Enter Your Backend Endpoint>";
    FirebaseInstanceId fcm;
    String FCM_token = null;
    
  5. I klassen MainActivity och metoden onCreate tar du bort eller kommenterar ut initieringen av fältet hub och anropet till registerWithNotificationHubs-metoden. Lägg sedan till kod för att initiera en instans av RegisterClient-klassen. Metoden ska innehålla följande rader:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        mainActivity = this;
        FirebaseService.createChannelAndHandleNotifications(getApplicationContext());
        fcm = FirebaseInstanceId.getInstance();
        registerClient = new RegisterClient(this, BACKEND_ENDPOINT);
        setContentView(R.layout.activity_main);
    }
    
  6. Lägg till följande import-uttryck i MainActivity.java-filen:

    import android.util.Base64;
    import android.view.View;
    import android.widget.EditText;
    
    import android.widget.Button;
    import android.widget.ToggleButton;
    import java.io.UnsupportedEncodingException;
    import android.content.Context;
    import java.util.HashSet;
    import android.widget.Toast;
    import org.apache.http.client.ClientProtocolException;
    import java.io.IOException;
    import org.apache.http.HttpStatus;
    
    import android.os.AsyncTask;
    import org.apache.http.HttpResponse;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.entity.StringEntity;
    import org.apache.http.impl.client.DefaultHttpClient;
    
    import android.app.AlertDialog;
    import android.content.DialogInterface;
    
    import com.google.firebase.iid.FirebaseInstanceId;
    import com.google.firebase.iid.InstanceIdResult;
    import com.google.android.gms.tasks.OnSuccessListener;
    import java.util.concurrent.TimeUnit;
    
  7. Ersätt koden i onStart-metoden med följande kod:

    super.onStart();
    Button sendPush = (Button) findViewById(R.id.sendbutton);
    sendPush.setEnabled(false);
    
  8. Lägg sedan till följande metoder för att hantera inloggningsknappen klicka på händelse och skicka push-meddelanden.

    public void login(View view) throws UnsupportedEncodingException {
        this.registerClient.setAuthorizationHeader(getAuthorizationHeader());
    
        final Context context = this;
        new AsyncTask<Object, Object, Object>() {
            @Override
            protected Object doInBackground(Object... params) {
                try {
    
                    FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(new OnSuccessListener<InstanceIdResult>() {
                        @Override
                        public void onSuccess(InstanceIdResult instanceIdResult) {
                            FCM_token = instanceIdResult.getToken();
                            Log.d(TAG, "FCM Registration Token: " + FCM_token);
                        }
                    });
                    TimeUnit.SECONDS.sleep(1);
                    registerClient.register(FCM_token, new HashSet<String>());
                } catch (Exception e) {
                    DialogNotify("MainActivity - Failed to register", e.getMessage());
                    return e;
                }
                return null;
            }
    
            protected void onPostExecute(Object result) {
                Button sendPush = (Button) findViewById(R.id.sendbutton);
                sendPush.setEnabled(true);
                Toast.makeText(context, "Signed in and registered.",
                        Toast.LENGTH_LONG).show();
            }
        }.execute(null, null, null);
    }
    
    private String getAuthorizationHeader() throws UnsupportedEncodingException {
        EditText username = (EditText) findViewById(R.id.usernameText);
        EditText password = (EditText) findViewById(R.id.passwordText);
        String basicAuthHeader = username.getText().toString()+":"+password.getText().toString();
        basicAuthHeader = Base64.encodeToString(basicAuthHeader.getBytes("UTF-8"), Base64.NO_WRAP);
        return basicAuthHeader;
    }
    
    /**
        * This method calls the ASP.NET WebAPI backend to send the notification message
        * to the platform notification service based on the pns parameter.
        *
        * @param pns     The platform notification service to send the notification message to. Must
        *                be one of the following ("wns", "fcm", "apns").
        * @param userTag The tag for the user who will receive the notification message. This string
        *                must not contain spaces or special characters.
        * @param message The notification message string. This string must include the double quotes
        *                to be used as JSON content.
        */
    public void sendPush(final String pns, final String userTag, final String message)
            throws ClientProtocolException, IOException {
        new AsyncTask<Object, Object, Object>() {
            @Override
            protected Object doInBackground(Object... params) {
                try {
    
                    String uri = BACKEND_ENDPOINT + "/api/notifications";
                    uri += "?pns=" + pns;
                    uri += "&to_tag=" + userTag;
    
                    HttpPost request = new HttpPost(uri);
                    request.addHeader("Authorization", "Basic "+ getAuthorizationHeader());
                    request.setEntity(new StringEntity(message));
                    request.addHeader("Content-Type", "application/json");
    
                    HttpResponse response = new DefaultHttpClient().execute(request);
    
                    if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                        DialogNotify("MainActivity - Error sending " + pns + " notification",
                                response.getStatusLine().toString());
                        throw new RuntimeException("Error sending notification");
                    }
                } catch (Exception e) {
                    DialogNotify("MainActivity - Failed to send " + pns + " notification ", e.getMessage());
                    return e;
                }
    
                return null;
            }
        }.execute(null, null, null);
    }
    

    login Hanteraren för knappen Logga in genererar en grundläggande autentiseringstoken med användarnamnet och lösenordet för indata (den representerar alla token som ditt autentiseringsschema använder) och använder RegisterClient sedan för att anropa serverdelen för registrering.

    sendPush-metoden anropar serverdelen för att utlösa ett säkert meddelande till användaren baserat på användartaggen. Plattformens meddelandetjänst som sendPush har som mål är beroende av pns-strängen som skickats.

  9. Lägg till följande DialogNotify-metod i klassen MainActivity.

    protected void DialogNotify(String title, String message)
    {
        AlertDialog alertDialog = new AlertDialog.Builder(MainActivity.this).create();
        alertDialog.setTitle(title);
        alertDialog.setMessage(message);
        alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "OK",
                new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                });
        alertDialog.show();
    }
    
  10. I din MainActivity-klass uppdaterar du sendNotificationButtonOnClick-metoden till att anropa sendPush-metoden med användarens valda plattformsmeddelandetjänster på följande sätt.

    /**
    * Send Notification button click handler. This method sends the push notification
    * message to each platform selected.
    *
    * @param v The view
    */
    public void sendNotificationButtonOnClick(View v)
            throws ClientProtocolException, IOException {
    
        String nhMessageTag = ((EditText) findViewById(R.id.editTextNotificationMessageTag))
                .getText().toString();
        String nhMessage = ((EditText) findViewById(R.id.editTextNotificationMessage))
                .getText().toString();
    
        // JSON String
        nhMessage = "\"" + nhMessage + "\"";
    
        if (((ToggleButton)findViewById(R.id.toggleButtonWNS)).isChecked())
        {
            sendPush("wns", nhMessageTag, nhMessage);
        }
        if (((ToggleButton)findViewById(R.id.toggleButtonFCM)).isChecked())
        {
            sendPush("fcm", nhMessageTag, nhMessage);
        }
        if (((ToggleButton)findViewById(R.id.toggleButtonAPNS)).isChecked())
        {
            sendPush("apns", nhMessageTag, nhMessage);
        }
    }
    
  11. I filen build.gradle lägger du till följande rad i avsnittet android efter avsnittet buildTypes.

    useLibrary 'org.apache.http.legacy'
    
  12. Om din app riktar in sig på API-nivå 28 (Android 9.0) eller senare tar du med följande deklaration i elementet <application>AndroidManifest.xmli .

    <uses-library
        android:name="org.apache.http.legacy"
        android:required="false" />
    
  13. Bygga projektet.

Testa appen

  1. Kör programmet på en enhet eller i en emulator med Android Studio.

  2. Ange ett användarnamn och lösenord i Android-appen. De måste båda ha samma strängvärde och får inte innehålla blanksteg eller specialtecken.

  3. I Android-appen klickar du på Logga in. Vänta på ett popup-meddelande med statusen Inloggad och registrerad. Detta aktiverar knappen Skicka meddelande.

    Screenshot of an emulator showing what the Notification Hubs Notify Users app looks like after logging in.

  4. Klicka på växlingsknapparna för att aktivera alla plattformar där du körde appen och registrerade en användare.

  5. Ange namnet på den användare som får meddelandet. Användaren måste vara registrerad för meddelanden på målenheterna.

  6. Ange ett meddelande som användaren ska få som ett push-meddelande.

  7. Klicka på Skicka meddelande. Varje enhet som har en registrering med matchande användarnamntagg får push-meddelandet.

Nästa steg

I den här självstudien har du lärt dig mer om push-meddelanden till specifika användare som har taggar associerade med sina registreringar. Information om hur du skickar platsbaserade meddelanden finns i nästa självstudie: