java - Flutter FCM后台调用平台特定代码

标签 java android flutter firebase-cloud-messaging background-process

FCM 后台处理程序无法调用在 MainActivity.java 中注册的特定于平台的代码自定义(遵循 flutter tutorial )。有什么办法可以让这个工作吗?我是flutter、java和android开发的新手,我的搜索已经走进了死胡同。

我可以强制打开设置了标志的应用程序,检查该标志,然后调用该方法吗?

是否有一些我可以在后台订阅和发送的事件/消息系统?

以下代码将产生以下输出:

I/flutter ( 6448): BACKGROUND_HANDLER::
I/flutter ( 6448): TRIGGERING ALARM::BEFORE
I/flutter ( 6448): TRIGGERING ALARM::AFTER
E/flutter ( 6448): [ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: MissingPluginException(No implementation found for method setAlarm on channel com.hybridalert.flutter_hybrid/alarm)
E/flutter ( 6448): #0      MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:314:7)
E/flutter ( 6448): <asynchronous suspension>
E/flutter ( 6448): #1      triggerAlarm (package:flutter_hybrid_alert/src/ui/home.dart:30:37)
E/flutter ( 6448): <asynchronous suspension>
E/flutter ( 6448): #2      backgroundHandle (package:flutter_hybrid_alert/src/ui/home.dart:67:5)
E/flutter ( 6448): #3      _fcmSetupBackgroundChannel.<anonymous closure> (package:firebase_messaging/firebase_messaging.dart:38:30)
E/flutter ( 6448): #4      _AsyncAwaitCompleter.start (dart:async-patch/async_patch.dart:43:6)
E/flutter ( 6448): #5      _fcmSetupBackgroundChannel.<anonymous closure> (package:firebase_messaging/firebase_messaging.dart:31:42)
E/flutter ( 6448): #6      MethodChannel._handleAsMethodCall (package:flutter/src/services/platform_channel.dart:397:55)
E/flutter ( 6448): #7      _AsyncAwaitCompleter.start (dart:async-patch/async_patch.dart:43:6)
E/flutter ( 6448): #8      MethodChannel._handleAsMethodCall (package:flutter/src/services/platform_channel.dart:394:39)
E/flutter ( 6448): #9      MethodChannel.setMethodCallHandler.<anonymous closure> (package:flutter/src/services/platform_channel.dart:365:54)
E/flutter ( 6448): #10     _DefaultBinaryMessenger.handlePlatformMessage (package:flutter/src/services/binary_messenger.dart:110:33)
E/flutter ( 6448): #11     _AsyncAwaitCompleter.start (dart:async-patch/async_patch.dart:43:6)
E/flutter ( 6448): #12     _DefaultBinaryMessenger.handlePlatformMessage (package:flutter/src/services/binary_messenger.dart:101:37)
E/flutter ( 6448): #13     _invoke3.<anonymous closure> (dart:ui/hooks.dart:293:15)
E/flutter ( 6448): #14     _rootRun (dart:async/zone.dart:1124:13)
E/flutter ( 6448): #15     _CustomZone.run (dart:async/zone.dart:1021:19)
E/flutter ( 6448): #16     _CustomZone.runGuarded (dart:async/zone.dart:923:7)
E/flutter ( 6448): #17     _invoke3 (dart:ui/hooks.dart:292:10)
E/flutter ( 6448): #18     _dispatchPlatformMessage (dart:ui/hooks.dart:154:5)
E/flutter ( 6448): 

home.dart

import "package:cloud_firestore/cloud_firestore.dart";
import "package:firebase_messaging/firebase_messaging.dart";
import "package:flutter/foundation.dart";
import "package:flutter/material.dart";
import "package:flutter/services.dart";
import "package:flutter_hybrid_alert/src/api/firestore.dart";
import "package:flutter_hybrid_alert/src/api/handlers.dart";
import "package:flutter_hybrid_alert/src/api/storage.dart";
import "package:flutter_hybrid_alert/src/models/models.dart";
import "package:flutter_hybrid_alert/src/routes.dart";
import "package:flutter_hybrid_alert/src/store/actions/auth_actions.dart";
import "package:flutter_hybrid_alert/src/store/actions/blacklist_actions.dart";
import "package:flutter_hybrid_alert/src/store/actions/user_actions.dart";
import "package:flutter_hybrid_alert/src/store/app_state.dart";
import "package:flutter_hybrid_alert/src/store/reducers/auth_reducer.dart";
import "package:flutter_hybrid_alert/src/ui/login.dart";
import "package:flutter_hybrid_alert/src/widgets/indicators/app_loading.dart";
import "package:flutter_local_notifications/flutter_local_notifications.dart";
import "package:flutter_redux/flutter_redux.dart";
import "package:nested_navigators/nested_nav_item.dart";
import "package:nested_navigators/nested_navigators.dart";
import "package:redux/redux.dart";

enum NestedNavItemKey { blacklist, recent, history, settings }

const platform = const MethodChannel("com.hybridalert.flutter_hybrid/alarm");

Future<void> triggerAlarm() async {
  try {
    final bool res = await platform.invokeMethod("setAlarm");
    print(res ? "Alarm triggered successfully." : "Alarm trigger failed.");
  } on PlatformException catch (e) {
    print("Error triggering alarm: \"${e.message}\".");
  }
}

/// showNotification - Init local notifications and show a new one.
void showNotification(String body) {
  final androidInitSettings = AndroidInitializationSettings("@mipmap/ic_launcher");
  final initSettings = InitializationSettings(androidInitSettings, null);
  final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
  flutterLocalNotificationsPlugin.initialize(initSettings);

  const String notificationChannel = "HybridAlert";
  final androidPlatformChannelSpecifics = AndroidNotificationDetails(
    notificationChannel,
    notificationChannel,
    "Alerts for Hybrid platform.",
    importance: Importance.Max,
    priority: Priority.High,
    ticker: "ticker",
  );

  final platformChannelSpecifics = NotificationDetails(androidPlatformChannelSpecifics, null);
  flutterLocalNotificationsPlugin.show(0, notificationChannel, body, platformChannelSpecifics);
}

Future<dynamic> backgroundHandle(Map<String, dynamic> message) {
  print("BACKGROUND_HANDLER::");
  if (message.containsKey("data")) {
    // Handle data message
    final dynamic data = message["data"];
    showNotification("BG:: " + data["name"]);

    // THIS THROWS THE ERROR
    print("TRIGGERING ALARM::BEFORE");
    triggerAlarm();
    print("TRIGGERING ALARM::AFTER");
  }

  return null;
}

/// handleNotifications - Force local notifications.
Future<dynamic> handleNotifications(Map<String, dynamic> message) async {
  print("NOTIFICATION_HANDLER::");
  if (message.containsKey("data")) {
    final dynamic data = message["data"];
    return showNotification("FG:: " + data["name"]);
  }

  final dynamic body = message["notification"]["body"];
  return showNotification(body);
}

Future<void> initFCM() async {
  final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
  _firebaseMessaging.requestNotificationPermissions();

  return _firebaseMessaging.getToken().then((token) {
    print("TOKEN:: $token");
    return handleSetUserToken(token);
  }).then((_) {
    return _firebaseMessaging.configure(
      onMessage: handleNotifications,
      onBackgroundMessage: backgroundHandle,
    );
  });
}

class Home extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _HomeState();
  }
}

triggerMainThread() async {
  try {
    final bool res = await platform.invokeMethod("setAlarm");
    print(res ? "Alarm triggered successfully." : "Alarm trigger failed.");
  } on PlatformException catch (e) {
    print("Error triggering alarm: \"${e.message}\".");
  }
}

class _HomeState extends State<Home> {
  AppUserSettings settings;
  String userId;
  Store store;

  @override
  void initState() {
    super.initState();

    // THIS WORKS FINE
    print("TRY_MAIN::BEFORE");
    triggerMainThread();
    print("TRY_MAIN::AFTER");

    initFCM();
    if (userId == null) {
      getStorageUser().then((String id) {
        store.dispatch(SetUserFBEmailAction(id));
        setState(() {
          userId = id;
        });
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    store = StoreProvider.of<AppState>(context);
    final AppState state = store.state;
    final Stream<DocumentSnapshot> userStream =
        userId != null ? usersRef.document(userId).get().asStream() : null;

    return StreamBuilder(
      stream: userStream,
      builder: (BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
        if (!snapshot.hasData || (state.loading.settings && state.auth.hybridAuthenticated)) {
          return buildAppLoadingIndicator("Loading settings...");
        }

        /*       Initialize User Store Data
         ======================================== */

        // Set hybrid email in store from firebase
        final AppUser appUser = AppUser.fromJson(snapshot.data.data);
        final List<String> stateBlacklist = state.blacklist;
        final List<String> blacklist = appUser.settings.blacklist;
        if (blacklist != null) {
          if (stateBlacklist != null) {
            // Compare the two
            final bool listsEqual = listEquals(blacklist, stateBlacklist);
            if (!listsEqual) {
              store.dispatch(SetBlacklistAction(blacklist));
            }
          } else {
            store.dispatch(SetBlacklistAction(blacklist));
          }
        }

        final String hybridEmail = appUser.email;
        final String stateHybridEmail = state.user.hybridEmail;
        final bool hybridEmailChanged = hybridEmail != stateHybridEmail;
        if (hybridEmailChanged) {
          store.dispatch(SetUserHybridEmailAction(hybridEmail));
        }

        // Set firebase userId as fbEmail.
        if (state.user.fbEmail == null) {
          store.dispatch(SetUserFBEmailAction(userId));
        }

        //  Build the Settings UI.
        return NestedNavigators(
          items: {
            NestedNavItemKey.recent: NestedNavigatorItem(
              initialRoute: Routes.recent,
              icon: Icons.home,
              text: "Recent",
            ),
            NestedNavItemKey.history: NestedNavigatorItem(
              initialRoute: Routes.history,
              icon: Icons.history,
              text: "History",
            ),
            NestedNavItemKey.blacklist: NestedNavigatorItem(
              initialRoute: Routes.blacklist,
              icon: Icons.list,
              text: "Blacklist",
            ),
            NestedNavItemKey.settings: NestedNavigatorItem(
              initialRoute: Routes.settings,
              icon: Icons.settings,
              text: "Settings",
            ),
          },
          generateRoute: (routeSettings) {
            return Routes.generateRoute(routeSettings, store.state);
          },
          buildBottomNavigationItem: (key, item, selected) => BottomNavigationBarItem(
            icon: Icon(
              item.icon,
//          color: Colors.blue,
            ),
            title: Text(
              item.text,
              style: TextStyle(
                fontSize: 18,
              ),
            ),
          ),
          bottomNavigationBarTheme: Theme.of(context).copyWith(
            splashColor: Colors.grey,
          ),
        );
      },
    );
  }
}

class AppHome extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return AppHomeState();
  }
}

class AppHomeState extends State {
  String hybridToken;

  @override
  void initState() {
    super.initState();
    getStorageToken().then((token) {
      setState(() {
        hybridToken = token;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return StoreConnector<AppState, AppState>(
      converter: (store) => store.state,
      builder: (BuildContext context, AppState state) {
        final AuthState authState = state.auth;
        final bool firebaseAuth = authState.firebaseAuthenticated;
        final bool hybridAuth = authState.hybridAuthenticated;
        if (!hybridAuth) {
          if (hybridToken != null && hybridToken != "") {
            final AuthState newState = AuthState(true, true);
            store.dispatch(SetAuthAction(newState));
          }
        }

        final bool isLoggedIn = hybridAuth && firebaseAuth;
        if (isLoggedIn) {
          return Home();
        }

        return LoginScreen();
      },
    );
  }
}

MainActivity.java

package com.hybridalert.flutter_hybrid_alert;

import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;

import io.flutter.app.FlutterActivity;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugins.GeneratedPluginRegistrant;

public class MainActivity extends FlutterActivity {
    private static final String CHANNEL = "com.hybridalert.flutter_hybrid/alarm";

    // I'm aware this is the default code from the flutter tutorial. Just testing at this point.
    private int setAlarm() {
        int batteryLevel = -1;
        if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
            BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);
            batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
        } else {
            Intent intent = new ContextWrapper(getApplicationContext()).
                    registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
            batteryLevel = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) /
                    intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
        }

        return batteryLevel;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        GeneratedPluginRegistrant.registerWith(this);
        new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
                new MethodCallHandler() {
                    @Override
                    public void onMethodCall(MethodCall call, Result result) {
                        System.out.println("CALL_METHOD::" + call.method);
                        // Note: this method is invoked on the main thread.
                        if (call.method.equals("setAlarm")) {
                            int batteryLevel = setAlarm();

                            if (batteryLevel != -1) {
                                result.success(true);
                            } else {
                                result.error("UNAVAILABLE", "Battery level not available.", null);
                            }
                        } else {
                            result.notImplemented();
                        }
                    }
                });

    }
}

最佳答案

如果您已完成https://pub.dev/packages/firebase_messaging中的所有步骤。然后将backgroundhandler函数放在main()之外。或者您可以创建一个带有静态的类,例如:

class BackgroundHandler {
  static Future<dynamic> backgroundHandler(Map<String, dynamic> message)  {
    if (message.containsKey('data')) {
      final dynamic data = message['data'];

    }

    if (message.containsKey('notification')) {
      final dynamic notification = message['notification'];
    }
  } 
}

关于java - Flutter FCM后台调用平台特定代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58036535/

相关文章:

java - java中使用泛型类型代替反射来创建实例

java - Tcp协议(protocol)和远程过程调用有什么区别?

android - React native,什么是 com.facebook.react.devsupport.DevSettingsActivity

dart - 如何强制flutter使用wifi而不是4G?

flutter - Flutter中给元素添加id

java - 从 bean 强制 Spring 关闭的最佳方法?

java - <T> 的 Mockito.any()

android - 用 ScrollView 替换两个 fragment 时出错

Android mapview 部分加载瓦片

google-maps - 如何从 flutter 中的谷歌地图获取位置的纬度和经度坐标?