【问题标题】:Run flutter code when android application class startsandroid应用程序类启动时运行flutter代码
【发布时间】:2019-12-08 13:40:15
【问题描述】:

我正在为 Flutter 制作一个插件,以使用 android 本机库处理 fcm 消息。

我们知道,当 FCM 接收到消息时,它会启动应用程序(它的应用程序类)并运行 Application#onCreate 块内的代码,因此我们可以在应用程序通过 fcm 在后台启动时运行本机代码。

我的问题是,是否可以在应用程序启动时运行颤振代码?
例如,如果收到消息:

应用类:

public class Application extends FlutterApplication {

  @Override
  public void onCreate() {
    super.onCreate();
    // Start flutter engine
    // Invoke a dart code in the Plugin using methodChannel or etc.
  }
}

【问题讨论】:

  • 使用 startActivity 启动你的主 Activity
  • 收到消息时无法启动用户的活动。
  • 你对此有何感想?当你有 Context 对象时,你可以随时这样做
  • 我并不是说我没有技术能力,这不是我要寻找的答案。我正在开发一个插件,是否启动一个活动,用户必须决定。
  • 对不起,我不知道你真正想要达到什么目的

标签: android flutter


【解决方案1】:

简短回答,是的

您可以使用 它的句柄键在后台调用 Dart 方法。

1。在后台注册您的插件

实现一个自定义应用程序类(覆盖FlutterApplication

public class MyApp extends FlutterApplication implements PluginRegistry.PluginRegistrantCallback {

    @Override
    public void registerWith(io.flutter.plugin.common.PluginRegistry registry) {
        // For apps using FlutterEmbedding v1
        GeneratedPluginRegistrant.registerWith(registry);
       // App with V2 will initialize plugins automatically, you might need to register your own however
    }
}

记得通过将android:name=".MyApp" 添加到<application> 属性来在AndroidManifest 中注册类。

What is embedding v2?

2。在你的颤振代码中创建一个设置函数作为顶级函数

/// Define this TopLevel or static
void _setup() async {
  MethodChannel backgroundChannel = const MethodChannel('flutter_background');
  // Setup Flutter state needed for MethodChannels.
  WidgetsFlutterBinding.ensureInitialized();

  // This is where the magic happens and we handle background events from the
  // native portion of the plugin.
  backgroundChannel.setMethodCallHandler((MethodCall call) async {
    if (call.method == 'handleBackgroundMessage') {
      final CallbackHandle handle =
          CallbackHandle.fromRawHandle(call.arguments['handle']);
      final Function handlerFunction =
          PluginUtilities.getCallbackFromHandle(handle);
      try {
        var dataArg = call.arguments['message'];
        if (dataArg == null) {
          print('Data received from callback is null');
          return;
        }
        await handlerFunction(dataArg);
      } catch (e) {
        print('Unable to handle incoming background message.\n$e');
      }
    }
    return Future.value();
  });

3。创建一个顶级回调,它将获取后台消息并调用它

_bgFunction(dynamic message) {
    // Message received in background
    // Remember, this will be a different isolate. So, no widgets
}

4。获取后台函数的handle key并设置并通过MethodChannel发送给native

// dart:ui needed
CallbackHandle setup PluginUtilities.getCallbackHandle(_setup);
CallbackHandle handle PluginUtilities.getCallbackHandle(_bgFunction);

_channel.invokeMethod<bool>(
  'handleFunction',
  <String, dynamic>{
    'handle': handle.toRawHandle(),
    'setup': setup.toRawHandle()
  },
);

5。将它们保存到本机端的 SharedPref 中

public void onMethodCall(MethodCall call, MethodChannel.Result result) {
  String methodName = call.method
  if (methodName == "handleFunction") {
     long handle = call.argument("handle");
     long setup = call.argument("setup");
     // save them
  }
}

6。后台唤醒后,启动后台隔离

FlutterMain.ensureInitializationComplete(context, null)
val appBundlePath = FlutterMain.findAppBundlePath()
val flutterCallback = FlutterCallbackInformation.lookupCallbackInformation(setupHandleYouHadSaved)

FlutterNativeView backgroundFlutterView = FlutterNativeView(context, true)

val args = FlutterRunArguments()
args.bundlePath = appBundlePath
args.entrypoint = flutterCallback.callbackName
args.libraryPath = flutterCallback.callbackLibraryPath

backgroundFlutterView?.runFromBundle(args)

// Initialize your registrant in the app class
pluginRegistrantCallback?.registerWith(backgroundFlutterView?.pluginRegistry)

7。当您的插件注册后,创建一个后台通道并将其传递给

val backgroundChannel = MethodChannel(messenger, "pushe_flutter_background")

8。调用将调用的 setup 方法并将消息给你回调

private fun sendBackgroundMessageToExecute(context: Context, message: String) {
    if (backgroundChannel == null) {
        return
    }

    val args: MutableMap<String, Any?> = HashMap()
    if (backgroundMessageHandle == null) {
        backgroundMessageHandle = getMessageHandle(context)
    }
    args["handle"] = backgroundMessageHandle
    args["message"] = message
    // The created background channel at step 7
    backgroundChannel?.invokeMethod("handleBackgroundMessage", args, null)
}

sendBackgroundMessageToExecute 将执行 dart _setup 函数并传递消息和回调句柄。在第 2 步中,回调将被调用。

注意:您可能仍需要考虑某些极端情况(例如线程等待和...)。查看示例并查看源代码。

当应用在后台启动时,有几个项目支持后台执行。

FirebaseMessaging

Pushe

WorkManager

【讨论】:

  • 这也是一个很好的答案。 firebase_messaging 也是这样做的,顺便说一句。
【解决方案2】:

Mahdi's answer 相比,我采用了一种不同的、更简单的方法。我避免定义额外的入口点/回调,使用PluginUtilities、回调句柄、在 SharedPreferences 中保存句柄、在 dart 和平台之间传递带有句柄的消息,或者实现 FlutterApplication

我正在开发一个颤振插件(所以如果你使用我的库来推送通知,你不必担心这个),所以我实现了FlutterPlugin。如果我想进行后台处理并且 Flutter 应用程序没有运行,我只需启动 Flutter 应用程序而不使用 Activity 或 View。这仅在 Android 上是必需的,因为 FlutterEngine/main dart 函数在 iOS 应用收到后台消息时已经运行。好处是这与 iOS 的行为相同:Flutter 应用在​​应用启动时始终在运行,即使没有向用户显示应用。

我使用以下命令启动应用程序:

flutterEngine = new FlutterEngine(context, null);
DartExecutor executor = flutterEngine.getDartExecutor();
backgroundMethodChannel = new MethodChannel(executor, "com.example.package.background");
backgroundMethodChannel.setMethodCallHandler(this);
// Get and launch the users app isolate manually:
executor.executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault());

我这样做是为了在库ably_flutter 中实现后台推送通知处理。它似乎运作良好。 FlutterEngine/ 应用程序仅在应用程序尚未运行时启动。我通过跟踪活动来做到这一点(使用ActivityAware):

    if (isApplicationInForeground) {
      // Send message to Dart side app already running
      Intent onMessageReceivedIntent = new Intent(PUSH_ON_MESSAGE_RECEIVED);
      onMessageReceivedIntent.putExtras(intent.getExtras());
      LocalBroadcastManager.getInstance(context).sendBroadcast(onMessageReceivedIntent);
    } else if (AblyFlutterPlugin.isActivityRunning) {
      // Flutter is already running, just send a background message to it.
      Intent onMessageReceivedIntent = new Intent(PUSH_ON_BACKGROUND_MESSAGE_RECEIVED);
      onMessageReceivedIntent.putExtras(intent.getExtras());
      LocalBroadcastManager.getInstance(context).sendBroadcast(onMessageReceivedIntent);
    } else {
      // No existing Flutter Activity is running, create a FlutterEngine and pass it the RemoteMessage
      new PushBackgroundIsolateRunner(context, asyncCompletionHandlerPendingResult, message);
    }

然后,我只使用一个单独的 MethodChannel 将消息传递回 dart 端。这种并行处理还有更多内容(比如告诉 Java 端应用程序正在运行/准备就绪。在代码库中搜索 call.method.equals(pushSetOnBackgroundMessage)。)。你可以在ably_flutter 看到更多关于实现PushBackgroundIsolateRunner.java 的信息。我还在广播接收器内部使用了goAsync,将执行时间从10s延长到30s,以与iOS 30s挂钟时间保持一致。

【讨论】:

  • 感谢本的回答。由于我的有点旧,而且我已经看到 Flutter 的那部分 API 发生了变化,所以你的答案值得一试?。
【解决方案3】:

您可以使用无头 Runner 从应用程序类(或服务、广播接收器等)运行 dart 代码。

有一篇关于如何实现这一点的深度文章:https://medium.com/flutter/executing-dart-in-the-background-with-flutter-plugins-and-geofencing-2b3e40a1a124

【讨论】:

    【解决方案4】:

    据我所知,我们必须调用一个类 GeneratedPluginRegistrant.registerWith(this);在必须运行颤振代码的 oncreate 方法中。

    【讨论】:

      【解决方案5】:

      如果你的意思是你想在后台运行一些任意的 Dart 代码,你可以使用我们创建的这个plugin,它确实有利于后台工作的使用。
      您可以注册一个应该在给定时间点执行的后台作业,它会回调您的Dart 代码,您可以在其中在后台运行一些代码。

      //Provide a top level function or static function.
      //This function will be called by Android and will return the value you provided when you registered the task.
      //See below
      void callbackDispatcher() {
        Workmanager.defaultCallbackDispatcher((echoValue) {
          print("Native echoed: $echoValue");
          return Future.value(true);
        });
      }
      
      Workmanager.initialize(callbackDispatcher)
      

      然后您可以安排它们。

      Workmanager.registerOneOffTask(
          "1", 
          "simpleTask"
      );
      

      String simpleTask 将在callbackDispatcher 函数开始在后台运行后返回。 这允许您安排多个后台作业并通过此 id 识别它们。

      【讨论】:

      • 不是我。但我的问题是如何在关闭后在后台启动颤振飞镖引擎。您的答案是针对引擎已经启动并且您想要后台工作的情况。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-12-31
      • 2015-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多