【发布时间】:2021-02-24 11:54:03
【问题描述】:
当我在 Flutter 中使用 hypertrack_plugin 和 firebase_messaging 时,我无法在 Android 中接收任何消息。但是,它在 iOS 中运行良好。
hypertrack_plugin 版本:0.1.3 firebase_messaging 版本:7.0.3
【问题讨论】:
标签: flutter firebase-cloud-messaging hypertrack
当我在 Flutter 中使用 hypertrack_plugin 和 firebase_messaging 时,我无法在 Android 中接收任何消息。但是,它在 iOS 中运行良好。
hypertrack_plugin 版本:0.1.3 firebase_messaging 版本:7.0.3
【问题讨论】:
标签: flutter firebase-cloud-messaging hypertrack
此问题已在 hypertrack_plugin: 0.1.4 版本的插件更新中修复
您正面临此问题,因为有多个从 FirebaseMessagingService 扩展的服务类。因此,消息在一个具有高优先级的类中接收,而不是在另一个类中接收。
将以下内容添加到您的 AndroidManifest.xml 文件中
<service android:name="io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService">
<intent-filter android:priority="100">
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
在其清单文件中为FlutterFirebaseMessagingService 设置的优先级为零(默认),但在其清单文件中声明HyperTrackMessagingService 的优先级为5(现在为4.8.0 版)。上面的解决方案只是覆盖了优先级,让传入的消息到达FlutterFirebaseMessagingService,而不是HyperTrackMessagingService。
虽然 HyperTrack 可以正常工作,但 HyperTrack 使用 FCM 进行设备-服务器通信以进行优化,如果没有 FCM,它将无法运行。但是,您可能没有注意到这一点。
使用反射将HyperTrackMessagingService 中的传入消息转发到插件hypertrack_plugin 中的FlutterFirebaseMessagingService。
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;
}
}
}
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>
MyFirebaseMessagingService 扩展自HyperTrackMessagingService,在AndroidManifest.xml 文件中声明,具有更高的优先级(绝对高于HyperTrackMessagingService)。这将允许消息直接进入新类。这也将消除方法 1 中的限制。现在我们也使用反射将消息转发到 FlutterFirebaseMessagingService。
没有限制,但需要在更新宿主库时手动更新hypertrack_plugin。 很高兴在插件本身中有这个更新,现在不存在(11 月 13 日)
现在我们不会触及库中的任何代码,而是编写我们自己的代码。我们将以安全的方式提出解决方案。您不必将 hypertrack_plugin 添加为本地依赖项。
<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;
}
}
}
<project_root>/couriers/android/app/src/main/AndroidManifest.xml中声明
<service
android:name=".MyFirebaseMessagingService"
android:exported="false">
<intent-filter android:priority="100">
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
我们明确地创建了MyFirebaseMessagingService 并从FirebaseMessagingService 扩展。我们还设置了高达 100 的优先级,以确保我们仅在此处接收传入消息,而不是在任何其他 Firebase 消息传递服务(如 HyperTrack)中接收。现在我们可以灵活地使用反射将这条消息转发到任何需要的地方。
无
【讨论】: