简体   繁体   English

Hypertrack 正在阻止 Flutter 中的 FCM 通知

[英]Hypertrack is preventing FCM notification in Flutter

When I use hypertrack_plugin along with firebase_messaging in Flutter, I am not able to receive any message in Android. However, it works fine in iOS.当我在 Flutter 中使用hypertrack_pluginfirebase_messaging时,我无法在 Android 中收到任何消息。但是,它在 iOS 中工作正常。

Version of hypertrack_plugin: 0.1.3 Version of firebase_messaging: 7.0.3 hypertrack_plugin 版本:0.1.3 firebase_messaging 版本:7.0.3

UPDATE更新

This issue has been fixed in the plugin update which is in the version hypertrack_plugin : 0.1.4此问题已在hypertrack_plugin版本的插件更新中修复:0.1.4

Problem问题

You are facing this issue because there is multiple service class that extends from FirebaseMessagingService .您遇到此问题是因为有多个服务 class 从FirebaseMessagingService扩展而来。 Because of this, messages are received in one class with high priority and not the other.因此,消息在一个 class 中以高优先级接收,而不是另一个。

Solution解决方案

Method 1方法一

Add the following to your AndroidManifest.xml file将以下内容添加到您的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>

How it works?怎么运行的?

The priority set for FlutterFirebaseMessagingService in its manifest file is zero (default) but HyperTrackMessagingService in its manifest file is declared with a priority of 5 (version 4.8.0 now).在其清单文件中为FlutterFirebaseMessagingService设置的优先级为零(默认),但在其清单文件中为HyperTrackMessagingService声明的优先级为 5(现在为 4.8.0 版)。 The above solution simply overrides the priority and lets incoming messages come to FlutterFirebaseMessagingService instead of HyperTrackMessagingService .上面的解决方案只是简单地覆盖了优先级,让传入的消息到达FlutterFirebaseMessagingService而不是HyperTrackMessagingService

Limitation:局限性:

Although HyperTrack will work fine, HyperTrack uses FCM for device-server communication for optimization which won't function without FCM.尽管 HyperTrack 可以正常工作,但 HyperTrack 使用 FCM 进行设备-服务器通信以进行优化,如果没有 FCM,则不会 function。 However, you might not notice this.但是,您可能没有注意到这一点。

Method 2方法二

Forward incoming message in HyperTrackMessagingService to FlutterFirebaseMessagingService in the plugin hypertrack_plugin using reflection.使用反射将HyperTrackMessagingService中的传入消息转发到插件FlutterFirebaseMessagingService中的hypertrack_plugin

Steps:脚步:

  1. You will need to download hypertrack_plugin source code and use it as a local dependency.您需要下载hypertrack_plugin源代码并将其用作本地依赖项。 How ? 怎么样
  2. Create a new class inside the library sdk-flutter/android/src/main/kotlin/com/hypertrack/sdk/flutter/MyFirebaseMessagingService.java在库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. Edit manifest file sdk-flutter/android/src/main/AndroidManifest.xml编辑清单文件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>

How it works?怎么运行的?

MyFirebaseMessagingService extended from HyperTrackMessagingService , is declared in the AndroidManifest.xml file with higher priority (definitely higher than HyperTrackMessagingService ). MyFirebaseMessagingService扩展自HyperTrackMessagingService ,在AndroidManifest.xml文件中声明了更高的优先级(肯定高于HyperTrackMessagingService )。 This will allow messages directly to come to the new class. This will also remove the limitation in method 1. Now we forward the message also to FlutterFirebaseMessagingService using reflection.这将允许消息直接到达新的 class。这也将消除方法 1 中的限制。现在我们也使用反射将消息转发到FlutterFirebaseMessagingService

Limitation:局限性:

No limitation, but you need to manually update the hypertrack_plugin when the host library is updated.不限,但需要在宿主库更新时手动更新hypertrack_plugin It's good to have this update in the plugin itself which is not present now (Nov 13)很高兴在现在不存在的插件本身中进行此更新(11 月 13 日)

Method 3方法三

Now we will not touch any code in the library but write our own code.现在我们不会接触库中的任何代码,而是编写我们自己的代码。 We will propose a solution in a safe way.我们将以安全的方式提出解决方案。 You don't have to add hypertrack_plugin as local dependencies.您不必将hypertrack_plugin添加为本地依赖项。

  1. Create class <project_root>/android/app/src/main/<your_package_name>/MyFirebaseMessagingService.java创建 class <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;
        }
    }
}
  1. Declare it in <project_root>/couriers/android/app/src/main/AndroidManifest.xml<project_root>/couriers/android/app/src/main/AndroidManifest.xml声明

Important: Set priority higher than 5 (one declared in HyperTrack SDK)重要:将优先级设置为高于 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>

How does it work?它是如何工作的?

We have created MyFirebaseMessagingService explicitly and extended from FirebaseMessagingService .我们明确地创建了MyFirebaseMessagingService并从FirebaseMessagingService进行了扩展。 We also have set a priority as high as 100 to make sure we are receiving incoming messages only here and not in any other firebase messaging service such as HyperTrack.我们还设置了高达 100 的优先级,以确保我们只在此处接收传入消息,而不会在任何其他 firebase 消息服务(例如 HyperTrack)中接收传入消息。 Now we have the flexibility to forward this message wherever we need, using reflection.现在我们可以灵活地使用反射将此消息转发到我们需要的任何地方。

Limitation:局限性:

None没有任何

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM