【问题标题】:Hypertrack is preventing FCM notification in FlutterHypertrack 正在阻止 Flutter 中的 FCM 通知
【发布时间】:2021-02-24 11:54:03
【问题描述】:

当我在 Flutter 中使用 hypertrack_pluginfirebase_messaging 时,我无法在 Android 中接收任何消息。但是,它在 iOS 中运行良好。

hypertrack_plugin 版本:0.1.3 firebase_messaging 版本:7.0.3

【问题讨论】:

    标签: flutter firebase-cloud-messaging hypertrack


    【解决方案1】:

    更新

    此问题已在 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

    步骤:

    1. 您需要下载hypertrack_plugin 源代码并将其用作本地依赖项。 How?
    2. 在库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;
            }
        }
    }
    
    1. 编辑清单文件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 日)

    方法3

    现在我们不会触及库中的任何代码,而是编写我们自己的代码。我们将以安全的方式提出解决方案。您不必将 hypertrack_plugin 添加为本地依赖项。

    1. 创建类&lt;project_root&gt;/android/app/src/main/&lt;your_package_name&gt;/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;
            }
        }
    }
    
    1. &lt;project_root&gt;/couriers/android/app/src/main/AndroidManifest.xml中声明

    重要提示:设置优先级高于 5(在 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>
    

    它是如何工作的?

    我们明确地创建了MyFirebaseMessagingService 并从FirebaseMessagingService 扩展。我们还设置了高达 100 的优先级,以确保我们仅在此处接收传入消息,而不是在任何其他 Firebase 消息传递服务(如 HyperTrack)中接收。现在我们可以灵活地使用反射将这条消息转发到任何需要的地方。

    限制:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-01-21
      • 2023-02-25
      • 2020-08-26
      • 2021-06-06
      • 2022-06-15
      • 2021-08-30
      • 2021-07-21
      相关资源
      最近更新 更多