Flutter FCM 后台调用平台具体代码
Posted
技术标签:
【中文标题】Flutter FCM 后台调用平台具体代码【英文标题】:Flutter FCM Background Call Platform Specific Code 【发布时间】:2020-01-21 23:52:16 【问题描述】: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();
);
【问题讨论】:
检查here。 同时检查GeoFencing使用服务注册插件。 @AbhayKoradiya 将"click_action": "FLUTTER_NOTIFICATION_CLICK"
添加到数据有效负载的第一个建议不会触发onMessage
或onResume
就像多个资源说它应该...我不知道是否或什么我做错了。您能否解释一下您建议的第二个链接(地理围栏)中发生了什么?
检查Flutter FCM ReadMe Guide。小心。它显示在第 4 点。
@AbhayKoradiya 我不确定你在说什么。在重新检查文档时,似乎确认这不是一个与我正在寻找的解决方案相匹配的解决方案。除非有我遗漏的东西,在这种情况下,请告诉我而不是神秘地告诉我。我需要应用程序在没有任何用户交互的情况下打开或移至前台。
【参考方案1】:
如果您已完成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'];
【讨论】:
以上是关于Flutter FCM 后台调用平台具体代码的主要内容,如果未能解决你的问题,请参考以下文章
当应用程序处于后台/终止并收到 FCM 通知时,如何在 Flutter 中显示本地通知?
如何在 Flutter 中使用 Agora 实现 Callkeep?