Esercitazione: Inviare notifiche push alle app Flutter usando Hub di notifica di Azure tramite un servizio back-end
Scaricare l' di esempio
- Xamarin.Forms
- Flutter
- React Native
In questa esercitazione si userà
Un back-end api Web core di
Queste operazioni vengono gestite usando l'SDK di Hub di notifica per le operazioni back-end. Altri dettagli sull'approccio generale sono disponibili nella documentazione Registrazione dal back-end dell'app.
Questa esercitazione illustra i passaggi seguenti:
- Configurare Servizi di notifica push e Hub di notifica di Azure.
- Creare un'applicazione back-end api Web di ASP.NET Core.
- Creare un'applicazione Flutter multipiattaforma.
- Configurare il progetto Android nativo per le notifiche push.
- Configurare il progetto iOS nativo per le notifiche push.
- Testare la soluzione.
Prerequisiti
Per seguire la procedura, è necessario:
- Una sottoscrizione di Azure in cui è possibile creare e gestire le risorse.
- Toolkit Flutter (insieme ai relativi prerequisiti).
Visual Studio Code con i plug-in Flutter e Dart installati. - CocoaPods installato per la gestione delle dipendenze della libreria.
- Possibilità di eseguire l'app in Android (dispositivi fisici o emulatori) o iOS (solo dispositivi fisici).
Per Android, è necessario disporre di:
- Un dispositivo fisico sbloccato dallo sviluppatore o un emulatore (che esegue l'API 26 e versioni successive con Google Play Services installato).
Per iOS, è necessario disporre di:
- Un account per sviluppatore Apple attivo .
- Un dispositivo iOS fisico registrato nell'account sviluppatore(che esegue iOS 13.0 e versioni successive).
- Un certificato di sviluppo
.p12 installato nel keychain che consente di eseguire un'app in un dispositivo fisico .
Nota
Il simulatore iOS non supporta le notifiche remote e pertanto è necessario un dispositivo fisico durante l'esplorazione di questo esempio in iOS. Tuttavia, non è necessario eseguire l'app in Android e iOS per completare questa esercitazione.
È possibile seguire i passaggi descritti in questo esempio di primo principio senza esperienza precedente. Tuttavia, si trarrà vantaggio dalla familiarità con gli aspetti seguenti.
- Portale per sviluppatori Apple.
- ASP.NET Core.
- Google Firebase Console.
- di Microsoft Azure e Inviare notifiche push alle app iOS usando Hub di notifica di Azure.
- flutter e Dart per lo sviluppo multipiattaforma.
- Kotlin e Swift per lo sviluppo nativo di Android e iOS.
I passaggi forniti sono specifici per macOS. È possibile seguire la procedura per
Configurare Servizi di notifica push e Hub di notifica di Azure
In questa sezione viene configurato Firebase Cloud Messaging (FCM) e Apple Push Notification Services (APNS). È quindi possibile creare e configurare un hub di notifica per l'uso con tali servizi.
Creare un progetto Firebase e abilitare Firebase Cloud Messaging per Android
Accedere alla console Firebase. Creare un nuovo progetto Firebase immettendo PushDemo come nome progetto.
Nota
Verrà generato automaticamente un nome univoco. Per impostazione predefinita, questo è costituito da una variante minuscola del nome specificato più un numero generato separato da un trattino. È possibile modificare questa impostazione se si desidera che sia ancora univoca a livello globale.
Dopo aver creato il progetto, selezionare Aggiungi Firebase all'app Android.
Nella pagina Aggiungi Firebase all'app Android seguire questa procedura.
Per il nome del pacchetto Android , immettere un nome per il pacchetto. Ad esempio:
com.<organization_identifier>.<package_name>
.Selezionare Registrare l'app.
Selezionare Scaricare google-services.json. Salvare quindi il file in una cartella locale per usarlo in un secondo momento e selezionare Avanti.
Selezionare Avanti.
Selezionare Continua nella console
Nota
Se il pulsante Continua nella console di
non è abilitato, a causa del controllo verifica installazione , scegliere Ignora questo passaggio .
Nella console firebase selezionare l'ingranaggio per il progetto. Selezionare quindi Impostazioni progetto.
Nota
Se il file di google-services.json non è stato scaricato, è possibile scaricarlo in questa pagina.
Passare alla scheda
Messaggistica cloud nella parte superiore. Copiare e salvare il chiave del serverper un uso successivo. Questo valore viene usato per configurare l'hub di notifica.
Registrare l'app iOS per le notifiche push
Per inviare notifiche push a un'app iOS, registrare l'applicazione con Apple e registrare anche le notifiche push.
Se l'app non è già stata registrata, passare al portale di provisioning iOS nel Centro per sviluppatori Apple. Accedere al portale con l'ID Apple, passare a certificati , identificatori & profilie quindi selezionare identificatori . Fare clic su + per registrare una nuova app.
Nella schermata Registra un nuovo identificatore selezionare il pulsante di opzione ID app . Selezionare quindi Continua.
Aggiornare i tre valori seguenti per la nuova app e quindi selezionare Continua:
Descrizione: digitare un nome descrittivo per l'app.
ID bundle: immettere un ID bundle del modulo com.<organization_identifier>.<product_name> come indicato nella Guida alla distribuzione di app . Nello screenshot seguente il valore
mobcat
viene usato come identificatore dell'organizzazione e il valore PushDemo viene usato come nome del prodotto.notifiche push: selezionare l'opzione notifiche push nella sezione Capabilities .Questa azione genera l'ID app e richiede di confermare le informazioni. Selezionare Continuae quindi selezionare Registra per confermare il nuovo ID app.
Dopo aver selezionato Registra, viene visualizzato il nuovo ID app come voce nella pagina Certificati, Identificatori & Profili.
Nella pagina Certificati, Identificatori & profili, in Identificatori individuare l'elemento della riga ID app creato. Selezionare quindi la riga per visualizzare la schermata Modifica configurazione ID app.
Creazione di un certificato per Hub di notifica
È necessario un certificato per consentire all'hub di notifica di funzionare con Apple Push Notification Services (APNS) e può essere fornito in uno dei due modi seguenti:
Creazione di un certificato push p12 che può essere caricato direttamente nell'hub di notifica (l'approccio originale)
Creazione di un certificato p8 che può essere usato per l'autenticazione basata su token (l'approccio più recente e consigliato)
L'approccio più recente offre numerosi vantaggi, come documentato in 'autenticazione basata su token (HTTP/2) per APNS. Sono necessari meno passaggi, ma viene richiesto anche per scenari specifici. Tuttavia, sono stati forniti passaggi per entrambi gli approcci, poiché entrambi funzioneranno ai fini di questa esercitazione.
OPZIONE 1: Creazione di un certificato push p12 che può essere caricato direttamente nell'hub di notifica
Nel Mac eseguire lo strumento Di accesso Keychain. Può essere aperto dalla cartella utilità di
o dalla cartella Altro nella finestra di avvio.Selezionare Keychain Access, espandere Assistente certificati e quindi selezionare Richiedere un certificato da un'autorità di certificazione.
Nota
Per impostazione predefinita, Keychain Access seleziona il primo elemento nell'elenco. Questo può essere un problema se ci si trova nella categoria certificati
e Apple Worldwide Developer Relations Certification Authority non è il primo elemento nell'elenco. Assicurarsi di avere un elemento non chiave o che sia selezionata l''Autorità di certificazione Apple Worldwide Developer Relations chiave, prima di generare la richiesta di firma del certificato.Selezionare l'indirizzo di posta elettronica utente
, immettere il valore nome comune , assicurarsi di specificare Salvato su disco e quindi selezionareContinua . Lasciare vuoto indirizzo di posta elettronica della CA perché non è obbligatorio.Immettere un nome per il file CSR (Certificate Signing Request) in Salva con nome, selezionare il percorso in Wheree quindi selezionare Salva.
Questa azione salva il file CSR nel percorso selezionato. Il percorso predefinito è Desktop. Ricordare il percorso scelto per il file.
Tornare alla pagina
certificati, identificatori & profili nel portale di provisioning iOS, scorrere verso il basso fino all'opzione notifiche push selezionata e quindi selezionare Configura per creare il certificato.Viene visualizzata
la finestra del servizio Apple Push Notification Service TLS/SSL. Selezionare il pulsanteCreate Certificate (Crea certificato TLS/SSL) nella sezioneDevelopment TLS/SSL Certificate (Certificato TLS/SSL). Viene visualizzata la schermata Crea un nuovo certificato.
Nota
Questa esercitazione usa un certificato di sviluppo. Lo stesso processo viene usato durante la registrazione di un certificato di produzione. Assicurarsi di usare lo stesso tipo di certificato durante l'invio di notifiche.
Selezionare Scegliere file, passare al percorso in cui è stato salvato il file CSR e quindi fare doppio clic sul nome del certificato per caricarlo. Selezionare quindi Continua.
Dopo aver creato il certificato nel portale, selezionare il pulsante Scarica. Salvare il certificato e ricordare il percorso in cui è stato salvato.
Il certificato viene scaricato e salvato nel computer nella cartella Download.
Nota
Per impostazione predefinita, il certificato di sviluppo scaricato è denominato aps_development.cer.
Fare doppio clic sul certificato push scaricato aps_development.cer. Questa azione installa il nuovo certificato nel Keychain, come illustrato nell'immagine seguente:
Nota
Anche se il nome nel certificato potrebbe essere diverso, il nome sarà preceduto da Apple Development iOS Push Services e a cui è associato l'identificatore del bundle appropriato.
In Accesso portachiavi
Controllo p12Fare clic su sul nuovo certificato push creato nella categoria certificati. Selezionare Esporta , denominare il file, selezionare il formatop12 e quindi selezionare Salva .È possibile scegliere di proteggere il certificato con una password, ma una password è facoltativa. Fare clic su OK se si desidera ignorare la creazione della password. Prendere nota del nome file e del percorso del certificato p12 esportato. Vengono usati per abilitare l'autenticazione con APN.
Nota
Il nome e il percorso del file p12 potrebbero essere diversi da quelli illustrati in questa esercitazione.
OPZIONE 2: Creazione di un certificato p8 che può essere usato per l'autenticazione basata su token
Prendere nota dei dettagli seguenti:
- prefisso ID app ( ID team)
- ID bundle
Tornare a certificati , identificatori & profili, fare clic su chiavi .
Nota
Se è già stata configurata una chiave per APNS, è possibile riutilizzare il certificato p8 scaricato subito dopo la creazione. In tal caso, è possibile ignorare i passaggi 3 fino a 5.
Fare clic sul pulsante + (o sul pulsante Crea una chiave) per creare una nuova chiave.
Specificare un valore nome chiave
appropriato, quindi selezionare l'opzione Apple Push Notifications Service (APNS) e quindi fare clic su Continue , seguita daRegister (Registra) nella schermata successiva.Fare clic su
Scarica e quindi spostare il filep8 (preceduto da AuthKey_ ) in una directory locale sicura, quindi fare clic suFine .Nota
Assicurarsi di mantenere il file p8 in un luogo sicuro (e salvare un backup). Dopo aver scaricato la chiave, non è possibile scaricarla nuovamente perché la copia del server viene rimossa.
In Chiavifare clic sulla chiave creata oppure su una chiave esistente se si è scelto di usarla.
Prendere nota del valore ID chiave
. Aprire il certificato p8 in un'applicazione appropriata a scelta, ad esempio Visual Studio Code. Prendere nota del valore della chiave (tra -----BEGIN PRIVATE KEY----- e -----END PRIVATE KEY-----).
-----BEGIN PRIVATE KEY-----
<key_value>
-----END PRIVATE KEY-----Nota
Si tratta del valore del token che verrà usato in un secondo momento per configurare Hub di notifica.
Al termine di questi passaggi, è necessario avere le informazioni seguenti per usarle più avanti in Configurare l'hub di notifica con informazioni APNS:
- ID team (vedere il passaggio 1)
- ID bundle (vedere il passaggio 1)
- ID chiave (vedere il passaggio 7)
- valore del token (valore della chiave p8 ottenuto nel passaggio 8)
Creare un profilo di provisioning per l'app
Tornare al portale di provisioning iOS , selezionare certificati , identificatori & profili, selezionare profili dal menu a sinistra e quindi selezionare + per creare un nuovo profilo. Viene visualizzata schermata Registra un nuovo profilo di provisioning.
Selezionare sviluppo di app iOS in Sviluppo come tipo di profilo di provisioning e quindi selezionare Continua.
Selezionare quindi l'ID app creato dall'elenco a discesa ID app e selezionare Continua.
Nella finestra Selezionare i certificati selezionare il certificato di sviluppo usato per la firma del codice e selezionare Continua.
Nota
Questo certificato non è il certificato push creato nel passaggio precedente . Questo è il certificato di sviluppo. Se non esiste, è necessario crearlo perché si tratta di un prerequisito per questa esercitazione. I certificati per sviluppatori possono essere creati nel portale per sviluppatori apple , tramite Xcode o in Visual Studio.
Tornare alla pagina di Certificati, Identificatori & Profili, selezionare Profili dal menu a sinistra e quindi selezionare + per creare un nuovo profilo. Viene visualizzata schermata Registra un nuovo profilo di provisioning.
Nella finestra Selezionare i certificati selezionare il certificato di sviluppo creato. Selezionare quindi Continua.
Selezionare quindi i dispositivi da usare per i test e selezionare Continua.
Infine, scegliere un nome per il profilo in Nome profilo di provisioninge selezionare Genera.
Quando viene creato il nuovo profilo di provisioning, selezionare Scarica. Ricordare la posizione in cui viene salvata.
Passare al percorso del profilo di provisioning e quindi fare doppio clic su di esso per installarlo nel computer di sviluppo.
Creare un hub di notifica
In questa sezione viene creato un hub di notifica e viene configurata l'autenticazione con APNS. È possibile usare un certificato push p12 o un'autenticazione basata su token. Se si vuole usare un hub di notifica già creato, è possibile passare al passaggio 5.
Accedere a Azure.
Fare clic su Crea una risorsa, quindi cercare e scegliere Hub di notifica, quindi fare clic su Crea.
Aggiornare i campi seguenti, quindi fare clic su Crea:
DETTAGLI DI BASE
sottoscrizione : Scegliere la sottoscrizione di destinazione dall'elenco a discesa
gruppo di risorse: Creare un nuovo gruppo di risorse (o selezionarne uno esistente)DETTAGLI SPAZIO DEI NOMI
spazio dei nomi dell'hub di notifica
: Immettere un nome univoco globale per lo spazio dei nomi dell'hub di notificaNota
Verificare che l'opzione Crea nuovo
sia selezionata per questo campo. DETTAGLI DELL'HUB DI NOTIFICA
Hub di notifica: Immettere un nome per l'hub di notifica
Località: Scegliere una posizione appropriata dall'elenco a discesa
piano tariffario: Mantenere l'opzionegratuita predefinita Nota
A meno che non sia stato raggiunto il numero massimo di hub nel livello gratuito.
Dopo aver effettuato il provisioning del
hub di notifica, passare a tale risorsa. Passare al nuovo hub di notifica .
Selezionare criteri di accesso dall'elenco (in MANAGE).
Prendere nota dei valori nome criterio
insieme ai valori stringa di connessione corrispondenti .
Configurare l'hub di notifica con informazioni APNS
In Notification Servicesselezionare Apple e quindi seguire i passaggi appropriati in base all'approccio scelto in precedenza nella sezione Creazione di un certificato per Hub di notifica.
Nota
Usa il di produzione
OPZIONE 1: Uso di un certificato push con estensione p12
Selezionare Certificato.
Selezionare l'icona del file.
Selezionare il file con estensione p12 esportato in precedenza e quindi selezionare Apri.
Se necessario, specificare la password corretta.
Selezionare modalità sandbox.
Selezionare Salva.
OPZIONE 2: Uso dell'autenticazione basata su token
Selezionare Token.
Immettere i valori seguenti acquisiti in precedenza:
- ID chiave
- ID bundle
- ID team
- token
Scegliere Sandbox.
Selezionare Salva.
Configurare l'hub di notifica con informazioni FCM
- Selezionare
Google (GCM/FCM) nella sezione impostazioni nel menu a sinistra. - Immettere la chiave del server annotata dal Google Firebase Console.
- Selezionare Salva sulla barra degli strumenti.
Creare un'applicazione back-end dell'API Web core ASP.NET
In questa sezione viene creato il back-end ASP.NET'API Web core per gestire registrazione del dispositivo e l'invio di notifiche all'app per dispositivi mobili Flutter.
Creare un progetto Web
In Visual Studioselezionare File>Nuova soluzione.
Selezionare >App>ASP.NET Core>API>Avanti.
Nella finestra di dialogo Configure your new ASP.NET Core Web API selezionare Target Framework di .NET Core 3.1.
Immettere
PushDemoApi per nome progetto e quindi selezionare Crea .Avviare il debug ( comando + Immettere) per testare l'app modello.
Nota
L'app modello è configurata per l'uso del WeatherForecastController
come launchUrl . Questa proprietà è impostata in Proprietà>launchSettings.json. Se viene visualizzato un certificato di sviluppo non valido trovato messaggio:
Fare clic su Sì per accettare di eseguire lo strumento "dotnet dev-certs https" per risolvere il problema. Lo strumento "dotnet dev-certs https" richiede quindi di immettere una password per il certificato e la password per il Keychain.
Fare clic su Sì quando viene richiesto di Installa e considerare attendibile il nuovo certificato, quindi immettere la password per il Keychain.
Espandere la cartella controller
, quindi eliminare WeatherForecastController.cs .Eliminare WeatherForecast.cs.
Configurare i valori di configurazione locale usando lo strumento Secret Manager. La separazione dei segreti dalla soluzione garantisce che non finisano nel controllo del codice sorgente. Aprire Terminale quindi passare alla directory del file di progetto ed eseguire i comandi seguenti:
dotnet user-secrets init dotnet user-secrets set "NotificationHub:Name" <value> dotnet user-secrets set "NotificationHub:ConnectionString" <value>
Sostituire i valori segnaposto con il nome dell'hub di notifica e i valori della stringa di connessione. Sono stati annoti nella sezione creare un hub di notifica. In caso contrario, è possibile cercarli in Azure.
NotificationHub:Name:
Vederename nel riepilogoEssentials nella parte superiore di Panoramica .NotificationHub:ConnectionString:
Vedere DefaultFullSharedAccessSignature in Criteri di accessoNota
Per gli scenari di produzione, è possibile esaminare opzioni come azure KeyVault per archiviare in modo sicuro la stringa di connessione. Per semplicità, i segreti verranno aggiunti alle impostazioni dell'applicazione servizio app di Azure.
Autenticare i client usando una chiave API (facoltativo)
Le chiavi API non sono sicure come i token, ma sono sufficienti ai fini di questa esercitazione. Una chiave API può essere configurata facilmente tramite il middleware ASP.NET.
Aggiungere la chiave API ai valori di configurazione locale.
dotnet user-secrets set "Authentication:ApiKey" <value>
Nota
È necessario sostituire il valore segnaposto con il proprio e prendere nota di esso.
Controllo Fare clic su nel progetto PushDemoApi, scegliere Nuova cartella dal menu Aggiungi, quindi fare clic su Aggiungi utilizzandoAuthentication comeNome cartella .Controllo Fare clic su nella cartella autenticazione, quindi scegliere Nuovo file... dal menu Aggiungi. Selezionare Generale>Classe vuota, immettere ApiKeyAuthOptions.cs per nome , quindi fare clic su Nuovo aggiungendo l'implementazione seguente.
using Microsoft.AspNetCore.Authentication; namespace PushDemoApi.Authentication { public class ApiKeyAuthOptions : AuthenticationSchemeOptions { public const string DefaultScheme = "ApiKey"; public string Scheme => DefaultScheme; public string ApiKey { get; set; } } }
Aggiungere un'altra
classe vuota alla cartella di autenticazione denominata ApiKeyAuthHandler.cs , quindi aggiungere l'implementazione seguente.using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Text.Encodings.Web; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace PushDemoApi.Authentication { public class ApiKeyAuthHandler : AuthenticationHandler<ApiKeyAuthOptions> { const string ApiKeyIdentifier = "apikey"; public ApiKeyAuthHandler( IOptionsMonitor<ApiKeyAuthOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) {} protected override Task<AuthenticateResult> HandleAuthenticateAsync() { string key = string.Empty; if (Request.Headers[ApiKeyIdentifier].Any()) { key = Request.Headers[ApiKeyIdentifier].FirstOrDefault(); } else if (Request.Query.ContainsKey(ApiKeyIdentifier)) { if (Request.Query.TryGetValue(ApiKeyIdentifier, out var queryKey)) key = queryKey; } if (string.IsNullOrWhiteSpace(key)) return Task.FromResult(AuthenticateResult.Fail("No api key provided")); if (!string.Equals(key, Options.ApiKey, StringComparison.Ordinal)) return Task.FromResult(AuthenticateResult.Fail("Invalid api key.")); var identities = new List<ClaimsIdentity> { new ClaimsIdentity("ApiKeyIdentity") }; var ticket = new AuthenticationTicket( new ClaimsPrincipal(identities), Options.Scheme); return Task.FromResult(AuthenticateResult.Success(ticket)); } } }
Aggiungere un'altra
classe vuota alla cartella di autenticazionedenominata ApiKeyAuthenticationBuilderExtensions.cs , quindi aggiungere l'implementazione seguente.using System; using Microsoft.AspNetCore.Authentication; namespace PushDemoApi.Authentication { public static class AuthenticationBuilderExtensions { public static AuthenticationBuilder AddApiKeyAuth( this AuthenticationBuilder builder, Action<ApiKeyAuthOptions> configureOptions) { return builder .AddScheme<ApiKeyAuthOptions, ApiKeyAuthHandler>( ApiKeyAuthOptions.DefaultScheme, configureOptions); } } }
Nota
Questo metodo di estensione semplifica il codice di configurazione del middleware in Startup.cs rendendolo più leggibile e generalmente più semplice da seguire.
In
Startup.cs aggiornare il metodo di ConfigureServicesper configurare l'autenticazione della chiave API sotto la chiamata ai servizi di . AddControllers metodo.using PushDemoApi.Authentication; using PushDemoApi.Models; using PushDemoApi.Services; public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddAuthentication(options => { options.DefaultAuthenticateScheme = ApiKeyAuthOptions.DefaultScheme; options.DefaultChallengeScheme = ApiKeyAuthOptions.DefaultScheme; }).AddApiKeyAuth(Configuration.GetSection("Authentication").Bind); }
Sempre in
Startup.cs aggiornare il metodoConfigure per chiamare il UseAuthenticatione i metodi di estensione UseAuthorization nelIApplicationBuilder dell'app . Assicurarsi che questi metodi vengano chiamati dopo UseRouting e prima di 'app. UseEndpoints.public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
Nota
La chiamata a UseAuthentication registra il middleware che usa gli schemi di autenticazione registrati in precedenza (da ConfigureServices). Questa operazione deve essere chiamata prima di qualsiasi middleware che dipende dall'autenticazione degli utenti.
Aggiungere dipendenze e configurare i servizi
ASP.NET Core supporta lo schema di progettazione software
L'uso dell'hub di notifica e dell'SDK di Hub di notifica per le operazioni back-end viene incapsulato all'interno di un servizio. Il servizio viene registrato e reso disponibile tramite un'astrazione appropriata.
Controllo Fare clic su nella cartella Dipendenze, quindi scegliere Gestisci pacchetti NuGet... .Cercare Microsoft.Azure.NotificationHubs e assicurarsi che sia selezionato.
Fare clic su Aggiungi pacchetti, quindi fare clic su Accetta quando viene richiesto di accettare le condizioni di licenza.
Controllo Fare clic su nel progetto PushDemoApiNuova cartella dal menu Aggiungi, quindi fare clic su Aggiungi utilizzandoModelli comeNome cartella .Controllo Fare clic su nella cartella Modelli di, quindi scegliere Nuovo file... dal menu Aggiungi. Selezionare >Classe vuota, immettere PushTemplates.cs per nome , quindi fare clic su Nuovo aggiungendo l'implementazione seguente.
namespace PushDemoApi.Models { public class PushTemplates { public class Generic { public const string Android = "{ \"notification\": { \"title\" : \"PushDemo\", \"body\" : \"$(alertMessage)\"}, \"data\" : { \"action\" : \"$(alertAction)\" } }"; public const string iOS = "{ \"aps\" : {\"alert\" : \"$(alertMessage)\"}, \"action\" : \"$(alertAction)\" }"; } public class Silent { public const string Android = "{ \"data\" : {\"message\" : \"$(alertMessage)\", \"action\" : \"$(alertAction)\"} }"; public const string iOS = "{ \"aps\" : {\"content-available\" : 1, \"apns-priority\": 5, \"sound\" : \"\", \"badge\" : 0}, \"message\" : \"$(alertMessage)\", \"action\" : \"$(alertAction)\" }"; } } }
Nota
Questa classe contiene i payload di notifica con token per le notifiche generiche e invisibile all'utente richieste da questo scenario. I payload vengono definiti al di fuori del 'installazione per consentire la sperimentazione senza dover aggiornare le installazioni esistenti tramite il servizio. La gestione delle modifiche alle installazioni in questo modo non rientra nell'ambito di questa esercitazione. Per l'ambiente di produzione, prendere in considerazione modelli personalizzati.
Aggiungere un'altra
classe vuota alla cartella modellidenominata DeviceInstallation.cs , quindi aggiungere l'implementazione seguente.using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace PushDemoApi.Models { public class DeviceInstallation { [Required] public string InstallationId { get; set; } [Required] public string Platform { get; set; } [Required] public string PushChannel { get; set; } public IList<string> Tags { get; set; } = Array.Empty<string>(); } }
Aggiungere un altro
classe vuota alla cartella modelli di denominata NotificationRequest.cs , quindi aggiungere l'implementazione seguente.using System; namespace PushDemoApi.Models { public class NotificationRequest { public string Text { get; set; } public string Action { get; set; } public string[] Tags { get; set; } = Array.Empty<string>(); public bool Silent { get; set; } } }
Aggiungere un altro
classe vuota alla cartella modelli di denominata NotificationHubOptions.cs , quindi aggiungere l'implementazione seguente.using System.ComponentModel.DataAnnotations; namespace PushDemoApi.Models { public class NotificationHubOptions { [Required] public string Name { get; set; } [Required] public string ConnectionString { get; set; } } }
Aggiungere una nuova cartella al progetto di
PushDemoApi denominato Services .Aggiungere un
dell'interfaccia vuota alla cartella Services denominata INotificationService.cs , quindi aggiungere l'implementazione seguente.using System.Threading; using System.Threading.Tasks; using PushDemoApi.Models; namespace PushDemoApi.Services { public interface INotificationService { Task<bool> CreateOrUpdateInstallationAsync(DeviceInstallation deviceInstallation, CancellationToken token); Task<bool> DeleteInstallationByIdAsync(string installationId, CancellationToken token); Task<bool> RequestNotificationAsync(NotificationRequest notificationRequest, CancellationToken token); } }
Aggiungere un classe vuota
alla cartella Services denominata NotificationHubsService.cs , quindi aggiungere il codice seguente per implementare l'interfacciaINotificationService :using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.NotificationHubs; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using PushDemoApi.Models; namespace PushDemoApi.Services { public class NotificationHubService : INotificationService { readonly NotificationHubClient _hub; readonly Dictionary<string, NotificationPlatform> _installationPlatform; readonly ILogger<NotificationHubService> _logger; public NotificationHubService(IOptions<NotificationHubOptions> options, ILogger<NotificationHubService> logger) { _logger = logger; _hub = NotificationHubClient.CreateClientFromConnectionString( options.Value.ConnectionString, options.Value.Name); _installationPlatform = new Dictionary<string, NotificationPlatform> { { nameof(NotificationPlatform.Apns).ToLower(), NotificationPlatform.Apns }, { nameof(NotificationPlatform.Fcm).ToLower(), NotificationPlatform.Fcm } }; } public async Task<bool> CreateOrUpdateInstallationAsync(DeviceInstallation deviceInstallation, CancellationToken token) { if (string.IsNullOrWhiteSpace(deviceInstallation?.InstallationId) || string.IsNullOrWhiteSpace(deviceInstallation?.Platform) || string.IsNullOrWhiteSpace(deviceInstallation?.PushChannel)) return false; var installation = new Installation() { InstallationId = deviceInstallation.InstallationId, PushChannel = deviceInstallation.PushChannel, Tags = deviceInstallation.Tags }; if (_installationPlatform.TryGetValue(deviceInstallation.Platform, out var platform)) installation.Platform = platform; else return false; try { await _hub.CreateOrUpdateInstallationAsync(installation, token); } catch { return false; } return true; } public async Task<bool> DeleteInstallationByIdAsync(string installationId, CancellationToken token) { if (string.IsNullOrWhiteSpace(installationId)) return false; try { await _hub.DeleteInstallationAsync(installationId, token); } catch { return false; } return true; } public async Task<bool> RequestNotificationAsync(NotificationRequest notificationRequest, CancellationToken token) { if ((notificationRequest.Silent && string.IsNullOrWhiteSpace(notificationRequest?.Action)) || (!notificationRequest.Silent && (string.IsNullOrWhiteSpace(notificationRequest?.Text)) || string.IsNullOrWhiteSpace(notificationRequest?.Action))) return false; var androidPushTemplate = notificationRequest.Silent ? PushTemplates.Silent.Android : PushTemplates.Generic.Android; var iOSPushTemplate = notificationRequest.Silent ? PushTemplates.Silent.iOS : PushTemplates.Generic.iOS; var androidPayload = PrepareNotificationPayload( androidPushTemplate, notificationRequest.Text, notificationRequest.Action); var iOSPayload = PrepareNotificationPayload( iOSPushTemplate, notificationRequest.Text, notificationRequest.Action); try { if (notificationRequest.Tags.Length == 0) { // This will broadcast to all users registered in the notification hub await SendPlatformNotificationsAsync(androidPayload, iOSPayload, token); } else if (notificationRequest.Tags.Length <= 20) { await SendPlatformNotificationsAsync(androidPayload, iOSPayload, notificationRequest.Tags, token); } else { var notificationTasks = notificationRequest.Tags .Select((value, index) => (value, index)) .GroupBy(g => g.index / 20, i => i.value) .Select(tags => SendPlatformNotificationsAsync(androidPayload, iOSPayload, tags, token)); await Task.WhenAll(notificationTasks); } return true; } catch (Exception e) { _logger.LogError(e, "Unexpected error sending notification"); return false; } } string PrepareNotificationPayload(string template, string text, string action) => template .Replace("$(alertMessage)", text, StringComparison.InvariantCulture) .Replace("$(alertAction)", action, StringComparison.InvariantCulture); Task SendPlatformNotificationsAsync(string androidPayload, string iOSPayload, CancellationToken token) { var sendTasks = new Task[] { _hub.SendFcmNativeNotificationAsync(androidPayload, token), _hub.SendAppleNativeNotificationAsync(iOSPayload, token) }; return Task.WhenAll(sendTasks); } Task SendPlatformNotificationsAsync(string androidPayload, string iOSPayload, IEnumerable<string> tags, CancellationToken token) { var sendTasks = new Task[] { _hub.SendFcmNativeNotificationAsync(androidPayload, tags, token), _hub.SendAppleNativeNotificationAsync(iOSPayload, tags, token) }; return Task.WhenAll(sendTasks); } } }
Nota
L'espressione di tag fornita per SendTemplateNotificationAsync è limitata a 20 tag. È limitato a 6 per la maggior parte degli operatori, ma l'espressione contiene solo ORs (||) in questo caso. Se nella richiesta sono presenti più di 20 tag, devono essere suddivisi in più richieste. Per altri dettagli, vedere la documentazione
routing ed espressioni di tag. In
Startup.cs aggiornare il metodo ConfigureServicesper aggiungere il NotificationHubsService come implementazione singleton di INotificationService .using PushDemoApi.Models; using PushDemoApi.Services; public void ConfigureServices(IServiceCollection services) { ... services.AddSingleton<INotificationService, NotificationHubService>(); services.AddOptions<NotificationHubOptions>() .Configure(Configuration.GetSection("NotificationHub").Bind) .ValidateDataAnnotations(); }
Creare l'API delle notifiche
Controllo Fare clic su nella cartella Controller, quindi scegliere Nuovo file... dal menu Aggiungi. Selezionare ASP.NET Core>Web API Controller Class, immettere NotificationsController per Name, quindi fare clic su New.
Nota
Se si segue con Visual Studio 2019, scegliere il modello Controller API con azioni di lettura/scrittura.
Aggiungere gli spazi dei nomi seguenti all'inizio del file.
using System.ComponentModel.DataAnnotations; using System.Net; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using PushDemoApi.Models; using PushDemoApi.Services;
Aggiornare il controller basato su modelli in modo che derivi da
ControllerBase ed è decorato con l'attributo ApiController. [ApiController] [Route("api/[controller]")] public class NotificationsController : ControllerBase { // Templated methods here }
Nota
La classe base Controller fornisce il supporto per le visualizzazioni, ma non è necessaria in questo caso e quindi è possibile usare ControllerBase. Se si segue con Visual Studio 2019, è possibile ignorare questo passaggio.
Se si sceglie di completare la sezione
Autenticare i client usando una chiave API , è necessario decorare anche l' NotificationsControllercon l'attributo Authorize . [Authorize]
Aggiornare il costruttore per accettare l'istanza registrata di INotificationService come argomento e assegnarlo a un membro readonly.
readonly INotificationService _notificationService; public NotificationsController(INotificationService notificationService) { _notificationService = notificationService; }
In
launchSettings.json (all'interno della cartella Proprietà), modificare il launchUrl da a api/notifications in modo che corrisponda all'URL specificato nell'attributo RegistrationsController Route. Avviare il debug (
Comando Invio ) per verificare che l'app funzioni con il nuovo NotificationsControllere restituisca uno stato di non autorizzato 401. Nota
Visual Studio potrebbe non avviare automaticamente l'app nel browser. Si userà Postman per testare l'API da questo punto in poi.
In una nuova scheda Postman impostare la richiesta su GET. Immettere l'indirizzo seguente sostituendo il segnaposto
con il httpsapplicationUrl applicationUrl trovato in Proprietà launchSettings.json .<applicationUrl>/api/notifications
Nota
Il applicationUrl deve essere 'https://localhost:5001' per il profilo predefinito. Se si usa
IIS (impostazione predefinita in Visual Studio 2019 in Windows), è consigliabile usare il applicationUrlspecificato nell'elemento iisSettings. Se l'indirizzo non è corretto, si riceverà una risposta 404. Se si sceglie di completare la sezione
Autenticare i client usando una chiave API , assicurarsi di configurare le intestazioni della richiesta per includere il valore apikey. Chiave Valore apikey <your_api_key> Fare clic sul pulsante invia
. Nota
Dovrebbe essere visualizzato uno stato
200 OK con alcuni contenuto JSON .Se viene visualizzato
un avviso di verifica certificato SSL, è possibile cambiare la richiesta di verifica del certificato SSL disattivata nellapostman Impostazioni .Sostituire i metodi della classe basato su modelli in NotificationsController.cs con il codice seguente.
[HttpPut] [Route("installations")] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType((int)HttpStatusCode.UnprocessableEntity)] public async Task<IActionResult> UpdateInstallation( [Required]DeviceInstallation deviceInstallation) { var success = await _notificationService .CreateOrUpdateInstallationAsync(deviceInstallation, HttpContext.RequestAborted); if (!success) return new UnprocessableEntityResult(); return new OkResult(); } [HttpDelete()] [Route("installations/{installationId}")] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType((int)HttpStatusCode.UnprocessableEntity)] public async Task<ActionResult> DeleteInstallation( [Required][FromRoute]string installationId) { var success = await _notificationService .DeleteInstallationByIdAsync(installationId, CancellationToken.None); if (!success) return new UnprocessableEntityResult(); return new OkResult(); } [HttpPost] [Route("requests")] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType((int)HttpStatusCode.UnprocessableEntity)] public async Task<IActionResult> RequestPush( [Required]NotificationRequest notificationRequest) { if ((notificationRequest.Silent && string.IsNullOrWhiteSpace(notificationRequest?.Action)) || (!notificationRequest.Silent && string.IsNullOrWhiteSpace(notificationRequest?.Text))) return new BadRequestResult(); var success = await _notificationService .RequestNotificationAsync(notificationRequest, HttpContext.RequestAborted); if (!success) return new UnprocessableEntityResult(); return new OkResult(); }
Creare l'app per le API
È ora possibile creare un app per le API
Fare clic su Crea una risorsa, quindi cercare e scegliere 'app per le API, quindi fare clic su Crea.
Aggiornare i campi seguenti, quindi fare clic su Crea.
nome app :
Immettere un nome univoco globale per l'app per le APISottoscrizione :
Scegliere la stessa destinazione Sottoscrizione creato l'hub di notifica in .gruppo di risorse :
Scegliere lo stesso gruppo di risorse in cui è stato creato l'hub di notifica.piano di servizio app/località:
Creare un nuovo piano di servizio appNota
Passare dall'opzione predefinita a un piano che include supporto SSL. In caso contrario, sarà necessario eseguire i passaggi appropriati quando si lavora con l'app per dispositivi mobili per impedire che le richieste http http vengano bloccate.
Application Insights:
Mantenere l'opzione suggerita (verrà creata una nuova risorsa usando tale nome) o selezionare una risorsa esistente.Dopo aver effettuato il provisioning del dell'app per le API
, passare a tale risorsa. Prendere nota della proprietà URL
nel riepilogo Essentials nella parte superiore della Panoramica . Questo URL è l'endpoint back-end che verrà usato più avanti in questa esercitazione.Nota
L'URL usa il nome dell'app per le API specificato in precedenza, con il formato
https://<app_name>.azurewebsites.net
.Selezionare Configurazione dall'elenco (in Impostazioni ).
Per ognuna delle impostazioni seguenti, fare clic su
Nuova impostazione applicazione per immettere il Nomee un Valore , quindi fare clic suOK .Nome Valore Authentication:ApiKey
<api_key_value> NotificationHub:Name
<hub_name_value> NotificationHub:ConnectionString
<hub_connection_string_value> Nota
Queste sono le stesse impostazioni definite in precedenza nelle impostazioni utente. Dovrebbe essere possibile copiarli. L'impostazione authentication:ApiKey
è necessaria solo se si sceglie di completare la sezione Autenticare i client usando una chiave API. Per gli scenari di produzione, è possibile esaminare opzioni come Azure KeyVault. Queste impostazioni sono state aggiunte come impostazioni dell'applicazione per semplicità in questo caso.Dopo aver aggiunto tutte le impostazioni dell'applicazione, fare clic su Salva, quindi Continua.
Pubblicare il servizio back-end
Distribuire quindi l'app nell'app per le API per renderla accessibile da tutti i dispositivi.
Nota
I passaggi seguenti sono specifici per Visual Studio per Mac. Se si segue con Visual Studio 2019 in Windows, il flusso di pubblicazione sarà diverso. Vedere Pubblicare nel servizio app di Azure in Windows.
Modificare la configurazione da Debug a Release, se non è già stato fatto.
Controllo Fare clic su il progetto PushDemoApie quindi scegliere Pubblica in Azure... dal menu Pubblica. Se richiesto, seguire il flusso di autenticazione. Usare l'account usato nella sezione precedente creare l'app per le API.
Selezionare la 'app per le API del servizio app di Azure creata in precedenza dall'elenco come destinazione di pubblicazione e quindi fare clic su Pubblica.
Dopo aver completato la procedura guidata, pubblica l'app in Azure e quindi apre l'app. Prendere nota dell'URL se non è già stato fatto. Questo URL è l'endpoint back-end usato più avanti in questa esercitazione.
Convalida dell'API pubblicata
In Postman aprire una nuova scheda, impostare la richiesta su PUT e immettere l'indirizzo seguente. Sostituire il segnaposto con l'indirizzo di base annotato nella sezione precedente pubblicare il servizio back-end.
https://<app_name>.azurewebsites.net/api/notifications/installations
Nota
L'indirizzo di base deve essere nel formato
https://<app_name>.azurewebsites.net/
Se si sceglie di completare la sezione
Autenticare i client usando una chiave API , assicurarsi di configurare le intestazioni della richiesta per includere il valore apikey. Chiave Valore apikey <your_api_key> Scegliere l'opzione
non elaborata per Body , quindi scegliereJSON dall'elenco di opzioni di formato e quindi includere alcuni segnaposto contenuto JSON :{}
Fare clic su Invia.
Nota
Si dovrebbe ricevere uno stato 422 UnprocessableEntity dal servizio.
Eseguire di nuovo i passaggi da 1 a 4, ma questa volta specificando l'endpoint delle richieste per convalidare la ricezione di una 400 richiesta non valida risposta.
https://<app_name>.azurewebsites.net/api/notifications/requests
Nota
Non è ancora possibile testare l'API usando dati di richiesta validi, perché saranno necessarie informazioni specifiche della piattaforma dall'app per dispositivi mobili client.
Creare un'applicazione Flutter multipiattaforma
In questa sezione viene creata una Flutter'applicazione per dispositivi mobili che implementa le notifiche push in modo multipiattaforma.
Consente di registrare e annullare la registrazione da un hub di notifica tramite il servizio back-end creato.
Un avviso viene visualizzato quando viene specificata un'azione e l'app è in primo piano. In caso contrario, le notifiche vengono visualizzate nel Centro notifiche.
Nota
In genere si eseguono le azioni di registrazione (e annullamento della registrazione) durante il punto appropriato nel ciclo di vita dell'applicazione (o come parte dell'esperienza di prima esecuzione) senza input espliciti di registrazione/annullamento della registrazione dell'utente. Tuttavia, questo esempio richiederà l'input esplicito dell'utente per consentire l'esplorazione e il test di questa funzionalità più facilmente.
Creare la soluzione Flutter
Aprire una nuova istanza di Visual Studio Code.
Aprire il riquadro comandi (MAIUSC + Comando + P).
Selezionare il comando Flutter: Nuovo progetto quindi premere INVIO.
Immettere
push_demo per nome progettoe quindi selezionare un percorso progetto .Quando richiesto, scegliere Recupera pacchetti.
Controllo Fare clic su nella cartellakotlin (in 'app src principale ), quindi scegliereReveal in Finder . Rinominare quindi le cartelle figlio (nella cartella kotlin) rispettivamente in , e . Nota
Quando si usa il modello di
di Visual Studio Code, queste cartelle vengono com ,esempio , . Supponendo che mobcat venga usato per l'organizzazione , la struttura di cartelle dovrebbe essere visualizzata in modo indicativo come:project_name - kotlin
- Com
- mobcat
- pushdemo
- mobcat
- Com
- kotlin
Tornare
Visual Studio Code , aggiornare il valore applicationIdin 'app android build.gradle in. Nota
È consigliabile usare il proprio nome dell'organizzazione per il segnaposto <your_organization>. Ad esempio, l'uso di mobcat perché l'organizzazione genera un nome di pacchetto valore di com.mobcat.pushdemo.
Aggiornare l'attributo pacchetto
nei file di AndroidManifest.xml , indebug ,src principale eprofilo src rispettivamente. Verificare che i valori corrispondano al applicationId usato nel passaggio precedente.<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.<your_organization>.pushdemo>"> ... </manifest>
Aggiornare l'attributo
android:label
nel file AndroidManifest.xml in src> principale in PushDemo. Aggiungere quindi l'attributoandroid:allowBackup
, direttamente inandroid:label
, impostandone il valore su false.<application android:name="io.flutter.app.FlutterApplication" android:label="PushDemo" android:allowBackup="false" android:icon="@mipmap/ic_launcher"> ... </application>
Aprire il file di
build.gradle a livello di app ( 'app android build.gradle ), quindi aggiornare il compileSdkVersion(dalla sezione android ) per usare l'API29 . Aggiornare quindi i valori minSdkVersion e targetSdkVersion (dalla sezione defaultConfig) rispettivamente a 26 e 29.Nota
Solo i dispositivi che eseguono livello API 26 e versioni successive sono supportati ai fini di questa esercitazione, ma è possibile estenderlo per supportare i dispositivi che eseguono versioni precedenti.
Controllo Fare clic su nella cartella ios, quindi scegliere Apri in Xcode .In
Xcode fare clic suRunner (xcodeproj nella parte superiore, non sulla cartella). Selezionare quindi la destinazione Runner e selezionare la scheda Generale . Con l'opzione Configurazione di compilazione selezionata, aggiornare il identificatore bundlein . Nota
È consigliabile usare il proprio nome dell'organizzazione per il segnaposto <your_organization>. Ad esempio, l'uso di
mobcat come l'organizzazione genererà un valore identificatore bundle di com.mobcat.PushDemo .Fare clic su
Info.plist quindi aggiornare il valore nome bundlePushDemo Chiudere Xcode e tornare a Visual Studio Code.
Tornare Visual Studio Code, aprire pubspec.yaml, aggiungere http e flutter_secure_storagepacchetti Dart come dipendenze. Salvare quindi il file e fare clic su Recupera pacchetti quando richiesto di farlo.
dependencies: flutter: sdk: flutter http: ^0.12.1 flutter_secure_storage: ^3.3.3
In
Terminale modificare la directory nella cartellaios (per il progetto Flutter). Eseguire quindi il comando di installazione del podper installare nuovi pod (richiesti dal pacchetto flutter_secure_storage ).Controllo Fare clic su nella cartella lib, quindi scegliere nuovo file dal menu usandomain_page.dart come nome file. Aggiungere quindi il codice seguente.import 'package:flutter/material.dart'; class MainPage extends StatefulWidget { @override _MainPageState createState() => _MainPageState(); } class _MainPageState extends State<MainPage> { @override Widget build(BuildContext context) { return Scaffold( body: SafeArea( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: <Widget>[], ) ) ); } }
In main.dartsostituire il codice basato su modelli con il codice seguente.
import 'package:flutter/material.dart'; import 'package:push_demo/main_page.dart'; final navigatorKey = GlobalKey<NavigatorState>(); void main() => runApp(MaterialApp(home: MainPage(), navigatorKey: navigatorKey));
In Terminalecompilare ed eseguire l'app in ogni piattaforma di destinazione per testare l'app modello eseguita nei dispositivi. Assicurarsi che i dispositivi supportati siano connessi.
flutter run
Implementare i componenti multipiattaforma
Controllo Fare clic su nella cartellalib, quindi scegliere nuova cartella dal menu usandomodelli come nome cartella. Controllo Fare clic su nella cartella modelli di, quindi scegliere nuovo file dal menu usandodevice_installation.dart come nome file. Aggiungere quindi il codice seguente.class DeviceInstallation { final String deviceId; final String platform; final String token; final List<String> tags; DeviceInstallation(this.deviceId, this.platform, this.token, this.tags); DeviceInstallation.fromJson(Map<String, dynamic> json) : deviceId = json['installationId'], platform = json['platform'], token = json['pushChannel'], tags = json['tags']; Map<String, dynamic> toJson() => { 'installationId': deviceId, 'platform': platform, 'pushChannel': token, 'tags': tags, }; }
Aggiungere un nuovo file alla cartella modelli di
denominata push_demo_action.dart definendo l'enumerazione delle azioni supportate in questo esempio.enum PushDemoAction { actionA, actionB, }
Aggiungere una nuova cartella al progetto denominato services quindi aggiungere un nuovo file a tale cartella denominata device_installation_service.dart con l'implementazione seguente.
import 'package:flutter/services.dart'; class DeviceInstallationService { static const deviceInstallation = const MethodChannel('com.<your_organization>.pushdemo/deviceinstallation'); static const String getDeviceIdChannelMethod = "getDeviceId"; static const String getDeviceTokenChannelMethod = "getDeviceToken"; static const String getDevicePlatformChannelMethod = "getDevicePlatform"; Future<String> getDeviceId() { return deviceInstallation.invokeMethod(getDeviceIdChannelMethod); } Future<String> getDeviceToken() { return deviceInstallation.invokeMethod(getDeviceTokenChannelMethod); } Future<String> getDevicePlatform() { return deviceInstallation.invokeMethod(getDevicePlatformChannelMethod); } }
Nota
È consigliabile usare il proprio nome dell'organizzazione per il segnaposto <your_organization>. Ad esempio, l'uso di
mobcat come l'organizzazione genererà un nome MethodChannel di com.mobcat.pushdemo/deviceinstallation .Questa classe incapsula l'uso della piattaforma nativa sottostante per acquisire i dettagli di installazione del dispositivo necessari. Un MethodChannel facilita la comunicazione asincrona bidirezionale con le piattaforme native sottostanti. La controparte specifica della piattaforma per questo canale verrà creata nei passaggi successivi.
Aggiungere un altro file alla cartella denominata notification_action_service.dart con l'implementazione seguente.
import 'package:flutter/services.dart'; import 'dart:async'; import 'package:push_demo/models/push_demo_action.dart'; class NotificationActionService { static const notificationAction = const MethodChannel('com.<your_organization>.pushdemo/notificationaction'); static const String triggerActionChannelMethod = "triggerAction"; static const String getLaunchActionChannelMethod = "getLaunchAction"; final actionMappings = { 'action_a' : PushDemoAction.actionA, 'action_b' : PushDemoAction.actionB }; final actionTriggeredController = StreamController.broadcast(); NotificationActionService() { notificationAction .setMethodCallHandler(handleNotificationActionCall); } Stream get actionTriggered => actionTriggeredController.stream; Future<void> triggerAction({action: String}) async { if (!actionMappings.containsKey(action)) { return; } actionTriggeredController.add(actionMappings[action]); } Future<void> checkLaunchAction() async { final launchAction = await notificationAction.invokeMethod(getLaunchActionChannelMethod) as String; if (launchAction != null) { triggerAction(action: launchAction); } } Future<void> handleNotificationActionCall(MethodCall call) async { switch (call.method) { case triggerActionChannelMethod: return triggerAction(action: call.arguments as String); default: throw MissingPluginException(); break; } } }
Nota
Viene usato come meccanismo semplice per centralizzare la gestione delle azioni di notifica in modo che possano essere gestite in modo multipiattaforma usando un'enumerazione fortemente tipizzata. Il servizio consente alla piattaforma nativa sottostante di attivare un'azione, quando ne viene specificata una nel payload di notifica. Consente inoltre al codice comune di controllare in modo retrospettivo se è stata specificata un'azione durante l'avvio dell'applicazione dopo che Flutter è pronto per elaborarlo. Ad esempio, quando l'app viene avviata toccando una notifica dal centro notifiche.
Aggiungere un nuovo file alla cartella dei servizi di
denominata notification_registration_service.dart con l'implementazione seguente.import 'dart:convert'; import 'package:flutter/services.dart'; import 'package:http/http.dart' as http; import 'package:push_demo/services/device_installation_service.dart'; import 'package:push_demo/models/device_installation.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; class NotificationRegistrationService { static const notificationRegistration = const MethodChannel('com.<your_organization>.pushdemo/notificationregistration'); static const String refreshRegistrationChannelMethod = "refreshRegistration"; static const String installationsEndpoint = "api/notifications/installations"; static const String cachedDeviceTokenKey = "cached_device_token"; static const String cachedTagsKey = "cached_tags"; final deviceInstallationService = DeviceInstallationService(); final secureStorage = FlutterSecureStorage(); String baseApiUrl; String apikey; NotificationRegistrationService(this.baseApiUrl, this.apikey) { notificationRegistration .setMethodCallHandler(handleNotificationRegistrationCall); } String get installationsUrl => "$baseApiUrl$installationsEndpoint"; Future<void> deregisterDevice() async { final cachedToken = await secureStorage.read(key: cachedDeviceTokenKey); final serializedTags = await secureStorage.read(key: cachedTagsKey); if (cachedToken == null || serializedTags == null) { return; } var deviceId = await deviceInstallationService.getDeviceId(); if (deviceId.isEmpty) { throw "Unable to resolve an ID for the device."; } var response = await http .delete("$installationsUrl/$deviceId", headers: {"apikey": apikey}); if (response.statusCode != 200) { throw "Deregister request failed: ${response.reasonPhrase}"; } await secureStorage.delete(key: cachedDeviceTokenKey); await secureStorage.delete(key: cachedTagsKey); } Future<void> registerDevice(List<String> tags) async { try { final deviceId = await deviceInstallationService.getDeviceId(); final platform = await deviceInstallationService.getDevicePlatform(); final token = await deviceInstallationService.getDeviceToken(); final deviceInstallation = DeviceInstallation(deviceId, platform, token, tags); final response = await http.put(installationsUrl, body: jsonEncode(deviceInstallation), headers: {"apikey": apikey, "Content-Type": "application/json"}); if (response.statusCode != 200) { throw "Register request failed: ${response.reasonPhrase}"; } final serializedTags = jsonEncode(tags); await secureStorage.write(key: cachedDeviceTokenKey, value: token); await secureStorage.write(key: cachedTagsKey, value: serializedTags); } on PlatformException catch (e) { throw e.message; } catch (e) { throw "Unable to register device: $e"; } } Future<void> refreshRegistration() async { final currentToken = await deviceInstallationService.getDeviceToken(); final cachedToken = await secureStorage.read(key: cachedDeviceTokenKey); final serializedTags = await secureStorage.read(key: cachedTagsKey); if (currentToken == null || cachedToken == null || serializedTags == null || currentToken == cachedToken) { return; } final tags = jsonDecode(serializedTags); return registerDevice(tags); } Future<void> handleNotificationRegistrationCall(MethodCall call) async { switch (call.method) { case refreshRegistrationChannelMethod: return refreshRegistration(); default: throw MissingPluginException(); break; } } }
Nota
Questa classe incapsula l'uso del DeviceInstallationService e le richieste al servizio back-end per eseguire le azioni di registrazione, annullamento della registrazione e aggiornamento necessarie. L'argomento apiKey
è necessario solo se si sceglie di completare la sezione Autenticare i client usando una chiave API.Aggiungere un nuovo file alla cartella lib
denominata config.dart con l'implementazione seguente.class Config { static String apiKey = "API_KEY"; static String backendServiceEndpoint = "BACKEND_SERVICE_ENDPOINT"; }
Nota
Viene usato come modo semplice per definire i segreti dell'app. Sostituire i valori segnaposto con i propri. Quando è stato creato il servizio back-end, è necessario prendere nota di questi elementi. Il URL dell'app per le API
deve essere apiKey. Il membro è necessario solo se si sceglie di completare la sezione Autenticare i client usando una chiave API.Assicurarsi di aggiungerlo al file gitignore per evitare di eseguire il commit di questi segreti nel controllo del codice sorgente.
Implementare l'interfaccia utente multipiattaforma
In
main_page.dart sostituire la funzione di compilazionecon quanto segue. @override Widget build(BuildContext context) { return Scaffold( body: Padding( padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 40.0), child: Column( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.stretch, children: <Widget>[ FlatButton( child: Text("Register"), onPressed: registerButtonClicked, ), FlatButton( child: Text("Deregister"), onPressed: deregisterButtonClicked, ), ], ), ), ); }
Aggiungere le importazioni necessarie all'inizio del file main_page.dart.
import 'package:push_demo/services/notification_registration_service.dart'; import 'config.dart';
Aggiungere un campo alla classe _MainPageState per archiviare un riferimento all'NotificationRegistrationService.
final notificationRegistrationService = NotificationRegistrationService(Config.backendServiceEndpoint, Config.apiKey);
Nella classe _MainPageState implementare i gestori eventi per gli eventi Register e Deregistereventi onPressed. Chiamare i metodi Register
Register corrispondenti e quindi visualizzare un avviso per indicare il risultato. void registerButtonClicked() async { try { await notificationRegistrationService.registerDevice(List<String>()); await showAlert(message: "Device registered"); } catch (e) { await showAlert(message: e); } } void deregisterButtonClicked() async { try { await notificationRegistrationService.deregisterDevice(); await showAlert(message: "Device deregistered"); } catch (e) { await showAlert(message: e); } } Future<void> showAlert({ message: String }) async { return showDialog<void>( context: context, barrierDismissible: false, builder: (BuildContext context) { return AlertDialog( title: Text('PushDemo'), content: SingleChildScrollView( child: ListBody( children: <Widget>[ Text(message), ], ), ), actions: <Widget>[ FlatButton( child: Text('OK'), onPressed: () { Navigator.of(context).pop(); }, ), ], ); }, ); }
Ora in main.dart, verificare che le importazioni seguenti siano presenti all'inizio del file.
import 'package:flutter/material.dart'; import 'package:push_demo/models/push_demo_action.dart'; import 'package:push_demo/services/notification_action_service.dart'; import 'package:push_demo/main_page.dart';
Dichiarare una variabile per archiviare il riferimento a un'istanza di NotificationActionService e inizializzarla.
final notificationActionService = NotificationActionService();
Aggiungere funzioni per gestire la visualizzazione di un avviso quando viene attivata un'azione.
void notificationActionTriggered(PushDemoAction action) { showActionAlert(message: "${action.toString().split(".")[1]} action received"); } Future<void> showActionAlert({ message: String }) async { return showDialog<void>( context: navigatorKey.currentState.overlay.context, barrierDismissible: false, builder: (BuildContext context) { return AlertDialog( title: Text('PushDemo'), content: SingleChildScrollView( child: ListBody( children: <Widget>[ Text(message), ], ), ), actions: <Widget>[ FlatButton( child: Text('OK'), onPressed: () { Navigator.of(context).pop(); }, ), ], ); }, ); }
Aggiornare la funzione principale
per osservare il flusso di NotificationActionService actionTriggered e verificare le eventuali azioni acquisite durante l'avvio dell'app.void main() async { runApp(MaterialApp(home: MainPage(), navigatorKey: navigatorKey,)); notificationActionService.actionTriggered.listen((event) { notificationActionTriggered(event as PushDemoAction); }); await notificationActionService.checkLaunchAction(); }
Nota
Questo è semplicemente per dimostrare la ricezione e la propagazione delle azioni di notifica push. In genere, questi vengono gestiti automaticamente, ad esempio passando a una visualizzazione specifica o aggiornando alcuni dati anziché visualizzare un avviso in questo caso.
Configurare il progetto Android nativo per le notifiche push
Aggiungere il file JSON di Google Services
Controllo Fare clic su nella cartella android, quindi scegliere Apri in Android Studio . Passare quindi alla visualizzazione Project (se non è già).Individuare il file
google-services.json scaricato in precedenza quando si configura il progetto PushDemonel Firebase Console . Trascinarlo quindi nella directory radice del modulo app( android android app ).
Configurare le impostazioni e le autorizzazioni di compilazione
Impostare la visualizzazione project
su Android .Aprire
AndroidManifest.xml , quindi aggiungere le autorizzazioni INTERNETe READ_PHONE_STATE dopo l'elemento'applicazione prima del tag di chiusura. <manifest> <application>...</application> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> </manifest>
Aggiungere gli SDK firebase
In Android Studioaprire il file build.gradle build.gradle (Gradle Scripts>build.gradle (Project: android)). e assicurarsi di avere il classpath "com.google.gms:google-services" nel nodo
buildscript
>dipendenze.buildscript { repositories { // Check that you have the following line (if not, add it): google() // Google's Maven repository } dependencies { // ... // Add the following line: classpath 'com.google.gms:google-services:4.3.3' // Google Services plugin } } allprojects { // ... repositories { // Check that you have the following line (if not, add it): google() // Google's Maven repository // ... } }
Nota
Assicurarsi di fare riferimento alla versione più recente in base alle istruzioni fornite nella
Firebase Console quando è stato creato il progetto Android .Nel file build.gradle a livello di app (Gradle Scripts>build.gradle (Module: app)), applicare il plug-in Gradle Google Services Gradle. Applicare il plug-in immediatamente sopra il nodo android.
// ... // Add the following line: apply plugin: 'com.google.gms.google-services' // Google Services plugin android { // ... }
Nello stesso file, nel nodo dipendenze aggiungere la dipendenza per la libreria Cloud Messaging Android.
dependencies { // ... implementation 'com.google.firebase:firebase-messaging:20.2.0' }
Salvare le modifiche, quindi fare clic sul pulsante sincronizza ora
(dal prompt della barra degli strumenti) o Sincronizza progetto con File Gradle .
Gestire le notifiche push per Android
In
Android Studio Controllo Fare clic su nelcom. (your_organization cartella del pacchetto pushdemo app src principale kotlin ), sceglierepacchetto dal menu Nuovo . Immettere servizi come nome, quindi premere Return.Controllo Fare clic su nella cartella dei servizi discegliere file/classe Kotlin dal menu Nuovo . Immettere DeviceInstallationService come nome, quindi premere Return.Implementare il DeviceInstallationService
usando il codice seguente. package com.<your_organization>.pushdemo.services import android.annotation.SuppressLint import android.content.Context import android.provider.Settings.Secure import com.google.android.gms.common.ConnectionResult import com.google.android.gms.common.GoogleApiAvailability import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel @SuppressLint("HardwareIds") class DeviceInstallationService { companion object { const val DEVICE_INSTALLATION_CHANNEL = "com.<your_organization>.pushdemo/deviceinstallation" const val GET_DEVICE_ID = "getDeviceId" const val GET_DEVICE_TOKEN = "getDeviceToken" const val GET_DEVICE_PLATFORM = "getDevicePlatform" } private var context: Context private var deviceInstallationChannel : MethodChannel val playServicesAvailable get() = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS constructor(context: Context, flutterEngine: FlutterEngine) { this.context = context deviceInstallationChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, DEVICE_INSTALLATION_CHANNEL) deviceInstallationChannel.setMethodCallHandler { call, result -> handleDeviceInstallationCall(call, result) } } fun getDeviceId() : String = Secure.getString(context.applicationContext.contentResolver, Secure.ANDROID_ID) fun getDeviceToken() : String { if(!playServicesAvailable) { throw Exception(getPlayServicesError()) } // TODO: Revisit once we have created the PushNotificationsFirebaseMessagingService val token = "Placeholder_Get_Value_From_FirebaseMessagingService_Implementation" if (token.isNullOrBlank()) { throw Exception("Unable to resolve token for FCM.") } return token } fun getDevicePlatform() : String = "fcm" private fun handleDeviceInstallationCall(call: MethodCall, result: MethodChannel.Result) { when (call.method) { GET_DEVICE_ID -> { result.success(getDeviceId()) } GET_DEVICE_TOKEN -> { getDeviceToken(result) } GET_DEVICE_PLATFORM -> { result.success(getDevicePlatform()) } else -> { result.notImplemented() } } } private fun getDeviceToken(result: MethodChannel.Result) { try { val token = getDeviceToken() result.success(token) } catch (e: Exception) { result.error("ERROR", e.message, e) } } private fun getPlayServicesError(): String { val resultCode = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context) if (resultCode != ConnectionResult.SUCCESS) { return if (GoogleApiAvailability.getInstance().isUserResolvableError(resultCode)){ GoogleApiAvailability.getInstance().getErrorString(resultCode) } else { "This device is not supported" } } return "An error occurred preventing the use of push notifications" } }
Nota
Questa classe implementa la controparte specifica della piattaforma per il canale
com.<your_organization>.pushdemo/deviceinstallation
. Questa operazione è stata definita nella parte Flutter dell'app all'interno di DeviceInstallationService.dart. In questo caso, le chiamate vengono effettuate dal codice comune all'host nativo. Assicurarsi di sostituire <your_organization> con la propria organizzazione ovunque venga usato.Questa classe fornisce un ID univoco (usando Secure.AndroidId) come parte del payload di registrazione dell'hub di notifica.
Aggiungere un altro
file/classe Kotlin alla cartella dei servizi di denominata NotificationRegistrationService , quindi aggiungere il codice seguente.package com.<your_organization>.pushdemo.services import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel class NotificationRegistrationService { companion object { const val NOTIFICATION_REGISTRATION_CHANNEL = "com.<your_organization>.pushdemo/notificationregistration" const val REFRESH_REGISTRATION = "refreshRegistration" } private var notificationRegistrationChannel : MethodChannel constructor(flutterEngine: FlutterEngine) { notificationRegistrationChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, NotificationRegistrationService.NOTIFICATION_REGISTRATION_CHANNEL) } fun refreshRegistration() { notificationRegistrationChannel.invokeMethod(REFRESH_REGISTRATION, null) } }
Nota
Questa classe implementa la controparte specifica della piattaforma per il canale
com.<your_organization>.pushdemo/notificationregistration
. Questa operazione è stata definita nella parte Flutter dell'app all'interno di NotificationRegistrationService.dart. In questo caso, le chiamate vengono effettuate dall'host nativo al codice comune. Anche in questo caso, prestare attenzione a sostituire <your_organization> con la propria organizzazione ovunque venga usato.Aggiungere un altro file/classe Kotlin alla cartella services denominata NotificationActionService, quindi aggiungere il codice seguente.
package com.<your_organization>.pushdemo.services import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel class NotificationActionService { companion object { const val NOTIFICATION_ACTION_CHANNEL = "com.<your_organization>.pushdemo/notificationaction" const val TRIGGER_ACTION = "triggerAction" const val GET_LAUNCH_ACTION = "getLaunchAction" } private var notificationActionChannel : MethodChannel var launchAction : String? = null constructor(flutterEngine: FlutterEngine) { notificationActionChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, NotificationActionService.NOTIFICATION_ACTION_CHANNEL) notificationActionChannel.setMethodCallHandler { call, result -> handleNotificationActionCall(call, result) } } fun triggerAction(action: String) { notificationActionChannel.invokeMethod(NotificationActionService.TRIGGER_ACTION, action) } private fun handleNotificationActionCall(call: MethodCall, result: MethodChannel.Result) { when (call.method) { NotificationActionService.GET_LAUNCH_ACTION -> { result.success(launchAction) } else -> { result.notImplemented() } } } }
Nota
Questa classe implementa la controparte specifica della piattaforma per il canale
com.<your_organization>.pushdemo/notificationaction
. Definito nella parte Flutter dell'app all'interno di NotificationActionService.dart. Le chiamate possono essere effettuate in entrambe le direzioni in questo caso. Assicurarsi di sostituire <your_organization> con la propria organizzazione ovunque venga usato.Aggiungere un nuovo file/classe Kotlin al com.<your_organization>pacchetto di .pushdemo denominato PushNotificationsFirebaseMessagingService, quindi implementare usando il codice seguente.
package com.<your_organization>.pushdemo import android.os.Handler import android.os.Looper import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.RemoteMessage import com.<your_organization>.pushdemo.services.NotificationActionService import com.<your_organization>.pushdemo.services.NotificationRegistrationService class PushNotificationsFirebaseMessagingService : FirebaseMessagingService() { companion object { var token : String? = null var notificationRegistrationService : NotificationRegistrationService? = null var notificationActionService : NotificationActionService? = null } override fun onNewToken(token: String) { PushNotificationsFirebaseMessagingService.token = token notificationRegistrationService?.refreshRegistration() } override fun onMessageReceived(message: RemoteMessage) { message.data.let { Handler(Looper.getMainLooper()).post { notificationActionService?.triggerAction(it.getOrDefault("action", null)) } } } }
Nota
Questa classe è responsabile della gestione delle notifiche quando l'app è in esecuzione in primo piano. Chiamerà in modo condizionale l'triggerAction nel NotificationActionService se un'azione viene inclusa nel payload di notifica ricevuto in onMessageReceived. Verrà anche chiamato
refreshRegistration nel NotificationRegistrationServicequando il token Firebase viene rigenerato eseguendo l'override della funzione onNewToken .Ancora una volta, prestare attenzione a sostituire <your_organization> con la propria organizzazione ovunque venga usato.
In
AndroidManifest.xml ('app src principale ), aggiungere ilPushNotificationsFirebaseMessagingService alla fine dell'elemento'applicazionecon il filtro finalità . <manifest> <application> <!-- EXISTING MANIFEST CONTENT --> <service android:name="com.<your_organization>.pushdemo.PushNotificationsFirebaseMessagingService" android:exported="false"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service> </application> </manifest>
Tornare in DeviceInstallationService, verificare che le importazioni seguenti siano presenti nella parte superiore del file.
package com.<your_organization>.pushdemo import com.<your_organization>.pushdemo.services.PushNotificationsFirebaseMessagingService
Nota
Sostituire <your_organization> con il valore dell'organizzazione.
Aggiornare il testo segnaposto Placeholder_Get_Value_From_FirebaseMessagingService_Implementation per ottenere il valore del token dal PushNotificationFirebaseMessagingService.
fun getDeviceToken() : String { if(!playServicesAvailable) { throw Exception(getPlayServicesError()) } // Get token from the PushNotificationsFirebaseMessagingService.token field. val token = PushNotificationsFirebaseMessagingService.token if (token.isNullOrBlank()) { throw Exception("Unable to resolve token for FCM.") } return token }
In MainActivityverificare che le importazioni seguenti siano presenti nella parte superiore del file.
package com.<your_organization>.pushdemo import android.content.Intent import android.os.Bundle import com.google.android.gms.tasks.OnCompleteListener import com.google.firebase.iid.FirebaseInstanceId import com.<your_organization>.pushdemo.services.DeviceInstallationService import com.<your_organization>.pushdemo.services.NotificationActionService import com.<your_organization>.pushdemo.services.NotificationRegistrationService import io.flutter.embedding.android.FlutterActivity
Nota
Sostituire <your_organization> con il valore dell'organizzazione.
Aggiungere una variabile per archiviare un riferimento all'DeviceInstallationService.
private lateinit var deviceInstallationService: DeviceInstallationService
Aggiungere una funzione denominata
processNotificationActions per verificare se un intentoha un valore aggiuntivo denominato 'azione . Attivare in modo condizionale l'azione o archiviarla per usarla in un secondo momento se l'azione viene elaborata durante l'avvio dell'app.private fun processNotificationActions(intent: Intent, launchAction: Boolean = false) { if (intent.hasExtra("action")) { var action = intent.getStringExtra("action"); if (action.isNotEmpty()) { if (launchAction) { PushNotificationsFirebaseMessagingService.notificationActionService?.launchAction = action } else { PushNotificationsFirebaseMessagingService.notificationActionService?.triggerAction(action) } } } }
Eseguire l'override della funzione onNewIntent per chiamare processNotificationActions.
override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) processNotificationActions(intent) }
Nota
Poiché
LaunchMode perMainActivity è impostato suSingleTop , un finalità diverrà inviato all'istanza attività esistente tramite il suNewIntent funzione anziché la funzioneonCreate e quindi è necessario gestire unintento in ingresso in entrambe le onCreare eonNewIntent .Eseguire l'override della funzione
onCreate , impostare il deviceInstallationService disu una nuova istanza di DeviceInstallationService .override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) flutterEngine?.let { deviceInstallationService = DeviceInstallationService(context, it) } }
Impostare le proprietà notificationActionService e notificationRegistrationService in PushNotificationFirebaseMessagingServices.
flutterEngine?.let { deviceInstallationService = DeviceInstallationService(context, it) PushNotificationsFirebaseMessagingService.notificationActionService = NotificationActionService(it) PushNotificationsFirebaseMessagingService.notificationRegistrationService = NotificationRegistrationService(it) }
Nella stessa funzione chiamare in modo condizionale FirebaseInstanceId.getInstance().instanceId. Implementare il onCompleteListener
per impostare il valore token di risultante in PushNotificationFirebaseMessagingService prima di chiamarerefreshRegistration .if(deviceInstallationService?.playServicesAvailable) { FirebaseInstanceId.getInstance().instanceId .addOnCompleteListener(OnCompleteListener { task -> if (!task.isSuccessful) return@OnCompleteListener PushNotificationsFirebaseMessagingService.token = task.result?.token PushNotificationsFirebaseMessagingService.notificationRegistrationService?.refreshRegistration() }) }
Sempre in onCreare, chiamare processNotificationActions alla fine della funzione. Usa true per l'argomento launchAction per indicare che questa azione viene elaborata durante l'avvio dell'app.
processNotificationActions(this.intent, true)
Nota
È necessario registrare nuovamente l'app ogni volta che viene eseguita e arrestarla da una sessione di debug per continuare a ricevere notifiche push.
Configurare il progetto iOS nativo per le notifiche push
Configurare la destinazione dello strumento di esecuzione e Info.plist
In
Visual Studio Code ,Controllo Fare clic su nella cartella ios, quindi scegliere Apri in Xcode .In
Xcode fare clic su Runner( xcodeproj nella parte superiore, non sulla cartella), quindi selezionare la destinazione Runner e quindi Funzionalità di firma & . Con l'opzione Configurazione di compilazione selezionata, scegliere l'account sviluppatore per Team. Verificare che l'opzione "Gestisci automaticamente la firma" sia selezionata e che il certificato di firma e il profilo di provisioning siano selezionati automaticamente.Nota
Se non viene visualizzato il nuovo valore profilo di provisioning, provare ad aggiornare i profili per l'identità di firma selezionando
Xcode Preferenze Account quindi selezionare il pulsante Scarica profili manualiper scaricare i profili. Fare clic su + Capability, quindi cercare Push Notifications.
fare doppio clic notifiche push per aggiungere questa funzionalità.Aprire Info.plist e impostare Versione minima di sistema su 13.0.
Nota
Solo i dispositivi che eseguono iOS 13.0 e versioni successive sono supportati ai fini di questa esercitazione, ma è possibile estenderlo per supportare i dispositivi che eseguono versioni precedenti.
Aprire
Runner.entitlements e assicurarsi che l'impostazione ambiente APS sia impostata su di sviluppo.
Gestire le notifiche push per iOS
Controllo Fare clic su nella cartella RunnerRunner (all'interno del progetto Runner), quindi scegliere Nuovo gruppo usandoServices come nome.Controllo Fare clic su nella cartella servizi, quindi scegliere Nuovo file... . Scegliere quindifile Swift e fare clic suAvanti . Specificare DeviceInstallationService per il nome e quindi fare clic su Crea.Implementare DeviceInstallationService.swift usando il codice seguente.
import Foundation class DeviceInstallationService { enum DeviceRegistrationError: Error { case notificationSupport(message: String) } var token : Data? = nil let DEVICE_INSTALLATION_CHANNEL = "com.<your_organization>.pushdemo/deviceinstallation" let GET_DEVICE_ID = "getDeviceId" let GET_DEVICE_TOKEN = "getDeviceToken" let GET_DEVICE_PLATFORM = "getDevicePlatform" private let deviceInstallationChannel : FlutterMethodChannel var notificationsSupported : Bool { get { if #available(iOS 13.0, *) { return true } else { return false } } } init(withBinaryMessenger binaryMessenger : FlutterBinaryMessenger) { deviceInstallationChannel = FlutterMethodChannel(name: DEVICE_INSTALLATION_CHANNEL, binaryMessenger: binaryMessenger) deviceInstallationChannel.setMethodCallHandler(handleDeviceInstallationCall) } func getDeviceId() -> String { return UIDevice.current.identifierForVendor!.description } func getDeviceToken() throws -> String { if(!notificationsSupported) { let notificationSupportError = getNotificationsSupportError() throw DeviceRegistrationError.notificationSupport(message: notificationSupportError) } if (token == nil) { throw DeviceRegistrationError.notificationSupport(message: "Unable to resolve token for APNS.") } return token!.reduce("", {$0 + String(format: "%02X", $1)}) } func getDevicePlatform() -> String { return "apns" } private func handleDeviceInstallationCall(call: FlutterMethodCall, result: @escaping FlutterResult) { switch call.method { case GET_DEVICE_ID: result(getDeviceId()) case GET_DEVICE_TOKEN: getDeviceToken(result: result) case GET_DEVICE_PLATFORM: result(getDevicePlatform()) default: result(FlutterMethodNotImplemented) } } private func getDeviceToken(result: @escaping FlutterResult) { do { let token = try getDeviceToken() result(token) } catch let error { result(FlutterError(code: "UNAVAILABLE", message: error.localizedDescription, details: nil)) } } private func getNotificationsSupportError() -> String { if (!notificationsSupported) { return "This app only supports notifications on iOS 13.0 and above. You are running \(UIDevice.current.systemVersion)" } return "An error occurred preventing the use of push notifications." } }
Nota
Questa classe implementa la controparte specifica della piattaforma per il canale
com.<your_organization>.pushdemo/deviceinstallation
. Questa operazione è stata definita nella parte Flutter dell'app all'interno di DeviceInstallationService.dart. In questo caso, le chiamate vengono effettuate dal codice comune all'host nativo. Assicurarsi di sostituire <your_organization> con la propria organizzazione ovunque venga usato.Questa classe fornisce un ID univoco (usando il valore UIDevice.identifierForVendor) come parte del payload di registrazione dell'hub di notifica.
Aggiungere un altro
file Swift alla cartellaServices denominata NotificationRegistrationService , quindi aggiungere il codice seguente.import Foundation class NotificationRegistrationService { let NOTIFICATION_REGISTRATION_CHANNEL = "com.<your_organization>.pushdemo/notificationregistration" let REFRESH_REGISTRATION = "refreshRegistration" private let notificationRegistrationChannel : FlutterMethodChannel init(withBinaryMessenger binaryMessenger : FlutterBinaryMessenger) { notificationRegistrationChannel = FlutterMethodChannel(name: NOTIFICATION_REGISTRATION_CHANNEL, binaryMessenger: binaryMessenger) } func refreshRegistration() { notificationRegistrationChannel.invokeMethod(REFRESH_REGISTRATION, arguments: nil) } }
Nota
Questa classe implementa la controparte specifica della piattaforma per il canale
com.<your_organization>.pushdemo/notificationregistration
. Questa operazione è stata definita nella parte Flutter dell'app all'interno di NotificationRegistrationService.dart. In questo caso, le chiamate vengono effettuate dall'host nativo al codice comune. Anche in questo caso, prestare attenzione a sostituire <your_organization> con la propria organizzazione ovunque venga usato.Aggiungere un altro
File Swift alla cartellaServices denominata NotificationActionService , quindi aggiungere il codice seguente.import Foundation class NotificationActionService { let NOTIFICATION_ACTION_CHANNEL = "com.<your_organization>.pushdemo/notificationaction" let TRIGGER_ACTION = "triggerAction" let GET_LAUNCH_ACTION = "getLaunchAction" private let notificationActionChannel: FlutterMethodChannel var launchAction: String? = nil init(withBinaryMessenger binaryMessenger: FlutterBinaryMessenger) { notificationActionChannel = FlutterMethodChannel(name: NOTIFICATION_ACTION_CHANNEL, binaryMessenger: binaryMessenger) notificationActionChannel.setMethodCallHandler(handleNotificationActionCall) } func triggerAction(action: String) { notificationActionChannel.invokeMethod(TRIGGER_ACTION, arguments: action) } private func handleNotificationActionCall(call: FlutterMethodCall, result: @escaping FlutterResult) { switch call.method { case GET_LAUNCH_ACTION: result(launchAction) default: result(FlutterMethodNotImplemented) } } }
Nota
Questa classe implementa la controparte specifica della piattaforma per il canale
com.<your_organization>.pushdemo/notificationaction
. Definito nella parte Flutter dell'app all'interno di NotificationActionService.dart. Le chiamate possono essere effettuate in entrambe le direzioni in questo caso. Assicurarsi di sostituire <your_organization> con la propria organizzazione ovunque venga usato.In AppDelegate.swiftaggiungere variabili per archiviare un riferimento ai servizi creati in precedenza.
var deviceInstallationService : DeviceInstallationService? var notificationRegistrationService : NotificationRegistrationService? var notificationActionService : NotificationActionService?
Aggiungere una funzione denominata processNotificationActions per l'elaborazione dei dati di notifica. Attivare in modo condizionale l'azione o archiviarla per usarla in un secondo momento se l'azione viene elaborata durante l'avvio dell'app.
func processNotificationActions(userInfo: [AnyHashable : Any], launchAction: Bool = false) { if let action = userInfo["action"] as? String { if (launchAction) { notificationActionService?.launchAction = action } else { notificationActionService?.triggerAction(action: action) } } }
Eseguire l'override della funzione di
didRegisterForRemoteNotificationsWithDeviceToken impostando il valore token diper il DeviceInstallationService . Chiamare quindi refreshRegistration nel NotificationRegistrationService.override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { deviceInstallationService?.token = deviceToken notificationRegistrationService?.refreshRegistration() }
Eseguire l'override della funzione didReceiveRemoteNotification
passando l'argomento userInfo alla funzione processNotificationActions . override func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) { processNotificationActions(userInfo: userInfo) }
Eseguire l'override della funzione didFailToRegisterForRemoteNotificationsWithError per registrare l'errore.
override func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { print(error); }
Nota
Questo è molto un segnaposto. È necessario implementare la registrazione e la gestione degli errori appropriati per gli scenari di produzione.
In
didFinishLaunchingWithOptions creare un'istanza delle variabilideviceInstallationService ,notificationRegistrationService enotificationActionService .let controller : FlutterViewController = window?.rootViewController as! FlutterViewController deviceInstallationService = DeviceInstallationService(withBinaryMessenger: controller.binaryMessenger) notificationRegistrationService = NotificationRegistrationService(withBinaryMessenger: controller.binaryMessenger) notificationActionService = NotificationActionService(withBinaryMessenger: controller.binaryMessenger)
Nella stessa funzione richiedere in modo condizionale l'autorizzazione e registrarsi per le notifiche remote.
if #available(iOS 13.0, *) { UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in if (granted) { DispatchQueue.main.async { let pushSettings = UIUserNotificationSettings(types: [.alert, .sound, .badge], categories: nil) application.registerUserNotificationSettings(pushSettings) application.registerForRemoteNotifications() } } } }
Se il
launchOptions contiene la chiave remoteNotification, chiamare processNotificationActions alla fine della funzionedidFinishLaunchingWithOptions. Passare l'oggetto userInfo risultante e usare true per l'argomento launchAction. Un valore true indica che l'azione viene elaborata durante l'avvio dell'app. if let userInfo = launchOptions?[.remoteNotification] as? [AnyHashable : Any] { processNotificationActions(userInfo: userInfo, launchAction: true) }
Testare la soluzione
È ora possibile testare l'invio di notifiche tramite il servizio back-end.
Inviare una notifica di test
Aprire una nuova scheda in Postman.
Impostare la richiesta su POSTe immettere l'indirizzo seguente:
https://<app_name>.azurewebsites.net/api/notifications/requests
Se si sceglie di completare la sezione
Autenticare i client usando una chiave API , assicurarsi di configurare le intestazioni della richiesta per includere il valore apikey. Chiave Valore apikey <your_api_key> Scegliere l'opzione
non elaborata per Body , quindi scegliereJSON dall'elenco di opzioni di formato e quindi includere alcuni segnaposto contenuto JSON :{ "text": "Message from Postman!", "action": "action_a" }
Selezionare il pulsante Codice
, che si trova sotto il pulsante Salva in alto a destra della finestra. La richiesta dovrebbe essere simile all'esempio seguente quando viene visualizzata perHTML (a seconda che sia stata inclusa un'intestazione apikey ): POST /api/notifications/requests HTTP/1.1 Host: https://<app_name>.azurewebsites.net apikey: <your_api_key> Content-Type: application/json { "text": "Message from backend service", "action": "action_a" }
Eseguire l'applicazione PushDemo di
in una o entrambe le piattaforme di destinazione ( Android e iOS ).Nota
Se si esegue il test in Android assicurarsi di non essere in esecuzione in Debugo se l'app è stata distribuita eseguendo l'applicazione, forzare la chiusura dell'app e avviarla nuovamente dall'utilità di avvio.
Nell'app PushDemo toccare il pulsante Registra.
Tornare in Postman, chiudere la finestra Genera frammenti di codice (se non è già stato fatto) quindi fare clic sul pulsante Invia.
Verificare di ottenere una risposta
200 OK in e che l'avviso venga visualizzato nell'app che mostraPostman 'azione ActionA ricevuta .Chiudere l'app PushDemo
, quindi fare di nuovo clic sul pulsante invia .Postman Verificare di ottenere una risposta di 200 OK in Postman di nuovo. Verificare che venga visualizzata una notifica nell'area di notifica per l'app PushDemo con il messaggio corretto.
Toccare la notifica per confermare che si apre l'app e visualizzare l'azione actionA ricevuta avviso.
Tornare in Postman, modificare il corpo della richiesta precedente per inviare una notifica invisibile all'utente specificando action_b anziché action_a per il valore dell'azione .
{ "action": "action_b", "silent": true }
Con l'app ancora aperta, fare clic sul pulsante invia
in .Postman Verificare di ottenere una risposta
200 OK in e che l'avviso venga visualizzato nell'app che mostraPostman 'azione ActionB ricevuta anziché'azione ActionA ricevuta .Chiudere l'app PushDemo
, quindi fare di nuovo clic sul pulsante invia .Postman Verificare di ottenere una risposta
200 OK in e che la notifica invisibile all'utente non venga visualizzata nell'area di notifica.Postman
Risoluzione dei problemi
Nessuna risposta dal servizio back-end
Quando si esegue il test in locale, assicurarsi che il servizio back-end sia in esecuzione e che usi la porta corretta.
Se si esegue il test sull'app per le API di Azure , verificare che il servizio sia in esecuzione e sia stato distribuito e avviato senza errori.
Assicurarsi di aver specificato correttamente l'indirizzo di base in Postman o nella configurazione dell'app per dispositivi mobili durante il test tramite il client. L'indirizzo di base deve essere indicativamente https://<api_name>.azurewebsites.net/
o https://localhost:5001/
durante il test in locale.
Non ricevere notifiche in Android dopo l'avvio o l'arresto di una sessione di debug
Assicurarsi di eseguire di nuovo la registrazione dopo l'avvio o l'arresto di una sessione di debug. Il debugger genererà la generazione di un nuovo token Firebase
Ricezione di un codice di stato 401 dal servizio back-end
Verificare che si stia impostando l'intestazione della richiesta apikey
Se questo errore viene visualizzato durante il test in locale, verificare che il valore della chiave definito nella configurazione client corrisponda al valore di autenticazione :ApiKey valore dell'impostazione utente usato dall'API .
Se si esegue il test con un'app per le API , assicurarsi che il valore della chiave nel file di configurazione del client corrisponda all'impostazione dell'applicazione Authentication:ApiKey usata nell'app per le API .
Nota
Se questa impostazione è stata creata o modificata dopo aver distribuito il servizio back-end, è necessario riavviare il servizio per renderla effettiva.
Se si è scelto di non completare la sezione
Ricezione di un codice di stato 404 dal servizio back-end
Verificare che l'endpoint e il metodo di richiesta HTTP siano corretti. Ad esempio, gli endpoint devono essere indicativamente:
-
[PUT]
https://<api_name>.azurewebsites.net/api/notifications/installations
-
[DELETE]
https://<api_name>.azurewebsites.net/api/notifications/installations/<installation_id>
-
[POST]
https://<api_name>.azurewebsites.net/api/notifications/requests
Oppure quando si esegue il test in locale:
-
[PUT]
https://localhost:5001/api/notifications/installations
-
[DELETE]
https://localhost:5001/api/notifications/installations/<installation_id>
-
[POST]
https://localhost:5001/api/notifications/requests
Quando si specifica l'indirizzo di base nell'app client, assicurarsi che termini con un /
. L'indirizzo di base deve essere indicativamente https://<api_name>.azurewebsites.net/
o https://localhost:5001/
durante il test in locale.
Impossibile registrare e viene visualizzato un messaggio di errore dell'hub di notifica
Verificare che il dispositivo di test disponga della connettività di rete. Determinare quindi il codice di stato della risposta Http impostando un punto di interruzione per esaminare il valore della proprietà
Esaminare i suggerimenti precedenti per la risoluzione dei problemi, se applicabili in base al codice di stato.
Impostare un punto di interruzione nelle righe che restituiscono questi codici di stato specifici per l'API corrispondente. Provare quindi a chiamare il servizio back-end durante il debug in locale.
Verificare che il servizio back-end funzioni come previsto tramite Postman usando il payload appropriato. Usare il payload effettivo creato dal codice client per la piattaforma in questione.
Esaminare le sezioni di configurazione specifiche della piattaforma per assicurarsi che non siano stati eseguiti passaggi. Verificare che i valori appropriati vengano risolti per le variabili installation id
e token
per la piattaforma appropriata.
Impossibile risolvere un ID per il messaggio di errore del dispositivo
Esaminare le sezioni di configurazione specifiche della piattaforma per assicurarsi che non siano stati eseguiti passaggi.
Collegamenti correlati
- panoramica di Hub di notifica di Azure
- Installazione di Flutter in macOS
- Installazione di Flutter in Windows
- SDK di Hub di notifica per le operazioni back-end
- SDK di Hub di notifica in GitHub
- Eseguire la registrazione con back-end dell'applicazione
- gestione della registrazione
- Uso di tag
- Uso di modelli personalizzati
Passaggi successivi
A questo momento dovrebbe essere connessa un'app Flutter di base a un hub di notifica tramite un servizio back-end e può inviare e ricevere notifiche.
Probabilmente sarà necessario adattare l'esempio usato in questa esercitazione per adattarsi al proprio scenario. È consigliabile implementare una gestione degli errori più affidabile, logica di ripetizione dei tentativi e registrazione.