Hypertrack impedisce la notifica FCM in Flutter
Quando utilizzo hypertrack_plugin insieme a firebase_messaging in Flutter, non riesco a ricevere alcun messaggio in Android. Tuttavia, funziona bene in iOS.
Versione di hypertrack_plugin: 0.1.3 Versione di firebase_messaging: 7.0.3
Risposte
AGGIORNARE
Questo problema è stato risolto nell'aggiornamento del plugin che si trova nella versione hypertrack_plugin : 0.1.4
Problema
Stai affrontando questo problema perché sono presenti più classi di servizi che si estendono da FirebaseMessagingService. Per questo motivo, i messaggi vengono ricevuti in una classe con priorità alta e non nell'altra.
Soluzione
Metodo 1
Aggiungi quanto segue al tuo AndroidManifest.xmlfile
<service android:name="io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService">
<intent-filter android:priority="100">
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
Come funziona?
La priorità impostata FlutterFirebaseMessagingServicenel file manifest è zero (impostazione predefinita) ma HyperTrackMessagingServicenel file manifest è dichiarata con una priorità 5 (versione 4.8.0 ora). La soluzione di cui sopra ignora semplicemente la priorità e consente ai messaggi in arrivo di arrivare al FlutterFirebaseMessagingServiceposto di HyperTrackMessagingService.
Limitazione:
Sebbene HyperTrack funzionerà correttamente, HyperTrack utilizza FCM per la comunicazione dispositivo-server per l'ottimizzazione che non funzionerà senza FCM. Tuttavia, potresti non accorgertene.
Metodo 2
Inoltra il messaggio in arrivo HyperTrackMessagingServicea FlutterFirebaseMessagingServicenel plug-in hypertrack_pluginutilizzando la riflessione.
Passaggi:
- Dovrai scaricare il codice sorgente hypertrack_plugin e usarlo come dipendenza locale. Come ?
- Crea una nuova classe all'interno della libreria
sdk-flutter/android/src/main/kotlin/com/hypertrack/sdk/flutter/MyFirebaseMessagingService.java
package com.hypertrack.sdk.flutter;
import android.annotation.SuppressLint;
import android.util.Log;
import com.google.firebase.messaging.RemoteMessage;
import com.hypertrack.sdk.HyperTrackMessagingService;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@SuppressLint("LongLogTag")
public class MyFirebaseMessagingService extends HyperTrackMessagingService {
private static final String TAG = "MyFirebaseMessagingService";
private Class<?> serviceClass;
private Object serviceObject;
public MyFirebaseMessagingService() {
try {
serviceClass = Class.forName("io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService");
serviceObject = serviceClass.newInstance();
injectContext();
Log.d(TAG, "io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService is found");
} catch (Throwable t) {
Log.w(TAG, "Can't find the class io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService", t);
}
}
@Override
public void onNewToken(final String s) {
super.onNewToken(s);
injectToken(s);
}
@Override
public void onMessageReceived(final RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
injectMessage(remoteMessage);
}
public void injectToken(String newToken) {
if (serviceClass != null) {
try {
Method sendTokenRefresh = serviceClass.getMethod("onNewToken", String.class);
sendTokenRefresh.invoke(serviceObject, newToken);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
Log.w(TAG, "Can't inject token due to error ", e);
}
}
}
public void injectMessage(RemoteMessage remoteMessage) {
if (serviceClass != null) {
try {
Method sendTokenRefresh = serviceClass.getMethod("onMessageReceived", RemoteMessage.class);
sendTokenRefresh.invoke(serviceObject, remoteMessage);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
Log.w(TAG, "Can't inject message due to error ", e);
}
}
}
private void injectContext() {
if (serviceObject != null) {
if (setField(serviceObject, "mBase", this)) {
Log.d(TAG, "context is set to io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService");
}
}
}
private boolean setField(Object targetObject, String fieldName, Object fieldValue) {
Field field;
try {
field = targetObject.getClass().getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
field = null;
}
Class<?> superClass = targetObject.getClass().getSuperclass();
while (field == null && superClass != null) {
try {
field = superClass.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
superClass = superClass.getSuperclass();
}
}
if (field == null) {
return false;
}
field.setAccessible(true);
try {
field.set(targetObject, fieldValue);
return true;
} catch (IllegalAccessException e) {
return false;
}
}
}
- Modifica il file manifest
sdk-flutter/android/src/main/AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.hypertrack.sdk.flutter">
<application>
<service
android:name=".MyFirebaseMessagingService"
android:exported="false" >
<intent-filter android:priority="100" >
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
</manifest>
Come funziona?
MyFirebaseMessagingServiceesteso da HyperTrackMessagingService, viene dichiarato nel AndroidManifest.xmlfile con priorità maggiore (decisamente maggiore di HyperTrackMessagingService). Ciò consentirà ai messaggi di arrivare direttamente alla nuova classe. Ciò rimuoverà anche la limitazione nel metodo 1. Ora inoltriamo il messaggio anche FlutterFirebaseMessagingServiceall'utilizzo della riflessione.
Limitazione:
Nessuna limitazione, ma è necessario aggiornare manualmente hypertrack_pluginquando viene aggiornata la libreria host. È bene avere questo aggiornamento nel plugin stesso che non è presente ora (13 novembre)
Metodo 3
Ora non toccheremo alcun codice nella libreria ma scriveremo il nostro codice. Proporremo una soluzione in modo sicuro. Non è necessario aggiungere hypertrack_plugincome dipendenze locali.
- Crea lezione
<project_root>/android/app/src/main/<your_package_name>/MyFirebaseMessagingService.java
package com.example.myapp;
import android.annotation.SuppressLint;
import android.util.Log;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@SuppressLint("LongLogTag")
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private static final String TAG = "MyFirebaseMessagingService";
// put all the firebase messaging service classes used in your project here
private String[] fcmClasses = new String[]{
"io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService",
"com.hypertrack.sdk.HyperTrackMessagingService"};
@Override
public void onNewToken(@NotNull String token) {
Log.d(TAG, "onNewToken()");
super.onNewToken(token);
injectToken(token);
}
@Override
public void onMessageReceived(@NotNull RemoteMessage remoteMessage) {
Log.d(TAG, "onMessageReceived()");
super.onMessageReceived(remoteMessage);
injectMessage(remoteMessage);
}
public void injectToken(String newToken) {
Log.d(TAG, "injectToken()");
for (String fcmClass : fcmClasses) {
try {
Class<?> serviceClass = Class.forName(fcmClass);
Object serviceObject = serviceClass.newInstance();
injectContext(serviceClass, serviceObject);
Method sendTokenRefresh = serviceClass.getMethod("onNewToken", String.class);
sendTokenRefresh.invoke(serviceObject, newToken);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | ClassNotFoundException | InstantiationException e) {
Log.w(TAG, "Can't inject token due to error ", e);
}
}
}
public void injectMessage(RemoteMessage remoteMessage) {
Log.d(TAG, "injectMessage()");
for (String fcmClass : fcmClasses) {
try {
Class<?> serviceClass = Class.forName(fcmClass);
Object serviceObject = serviceClass.newInstance();
injectContext(serviceClass, serviceObject);
Method sendTokenRefresh = serviceClass.getMethod("onMessageReceived", RemoteMessage.class);
sendTokenRefresh.invoke(serviceObject, remoteMessage);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | ClassNotFoundException | InstantiationException e) {
Log.w(TAG, "Can't inject token due to error ", e);
}
}
}
private void injectContext(Class<?> serviceClass, Object serviceObject) {
Log.d(TAG, "injectContext()");
if (serviceClass != null) {
if (setField(serviceObject, "mBase", this)) {
Log.d(TAG, "context is set to " + serviceClass.getName());
}
}
}
private boolean setField(Object targetObject, String fieldName, Object fieldValue) {
Log.d(TAG, "setField()");
Field field;
try {
field = targetObject.getClass().getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
field = null;
}
Class<?> superClass = targetObject.getClass().getSuperclass();
while (field == null && superClass != null) {
try {
field = superClass.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
superClass = superClass.getSuperclass();
}
}
if (field == null) {
return false;
}
field.setAccessible(true);
try {
field.set(targetObject, fieldValue);
return true;
} catch (IllegalAccessException e) {
return false;
}
}
}
- Dichiaralo in
<project_root>/couriers/android/app/src/main/AndroidManifest.xml
Importante: imposta una priorità superiore a 5 (una dichiarata in HyperTrack SDK)
<service
android:name=".MyFirebaseMessagingService"
android:exported="false">
<intent-filter android:priority="100">
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
Come funziona?
Abbiamo creato MyFirebaseMessagingServiceesplicitamente ed esteso da FirebaseMessagingService. Abbiamo anche impostato una priorità fino a 100 per assicurarci di ricevere i messaggi in arrivo solo qui e non in qualsiasi altro servizio di messaggistica Firebase come HyperTrack. Ora abbiamo la flessibilità di trasmettere questo messaggio ovunque sia necessario, utilizzando la riflessione.
Limitazione:
Nessuna