Flutter:在应用程序关闭或在后台运行原生 android 代码

Posted

技术标签:

【中文标题】Flutter:在应用程序关闭或在后台运行原生 android 代码【英文标题】:Flutter: Run native android code while app closed or in background 【发布时间】:2021-11-22 06:27:25 【问题描述】:

我正在使用 Firebase 和颤动本地通知来显示我从服务器获得的通知。我想在 android 通知的对话部分中显示某些通知,因此我使用方法通道进行了原生 kotlin 调用来为此创建快捷方式:

飞镖

  static Future<String?> _createConversationShortcut(
      String personName, String personIconPath) async 
    const platform = MethodChannel('...');
    String result;
    try 
      result = await platform.invokeMethod('createConversationShortcut', 
        'personName': personName,
        'personIcon': personIconPath,
      );
     on PlatformException catch (e) 
      return null;
    
    return result;
  

我调用方法_createConversationShortcut原生创建快捷方式,然后在AndroidNotificationDetails中使用快捷方式ID,使通知在Conversation部分

      flutterLocalNotificationsPlugin.show(
        int.parse(person.key!),
        notification,
        text,
        NotificationDetails(
          android: AndroidNotificationDetails(
            channel.id,
            channel.name,
            channel.description,
            icon: '@drawable/is_notification',
            category: 'msg',
            shortcutId: shortcutId,
            styleInformation: MessagingStyleInformation(
              person,
              groupConversation: false,
              conversationTitle: 'Neue Privatnachricht',
              messages: messages,
            ),
            //setAsGroupSummary: true,
          ),
        ),
        payload: json.encode(payload),
      );

科特林

class MainActivity: FlutterActivity() 

    private val CHANNEL = "..."

    private fun createConversationShortcut(personName: String, personIcon: String): String 
        val person = Person.Builder()
                .setName(personName)
                .setIcon(IconCompat.createWithContentUri(personIcon))
                .build()
        val shortcut = ShortcutInfoCompat.Builder(context, personName.toLowerCase(Locale.ROOT).replace(" ", "_") + "_shortcut")
                .setLongLived()
                .setIntent(Intent(Intent.ACTION_VIEW))
                .setPerson(person)
                .setShortLabel(personName)
                .build()
        ShortcutManagerCompat.addDynamicShortcuts(context, listOf(shortcut))
        return shortcut.id
    

    override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) 
        super.configureFlutterEngine(flutterEngine)
        GeneratedPluginRegistrant.registerWith(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler  call, result ->
            if (call.method == "createConversationShortcut") 
                val personName = call.argument<String>("personName")
                val personIcon = call.argument<String>("personIcon")
                if (personName != null && personIcon != null) 
                    val shortcutId = createConversationShortcut(personName, personIcon)
                    result.success(shortcutId)
                 else 
                    result.error("argument-error", "Too few arguments", null)
                
             else 
                result.notImplemented()
            
        
    

当应用程序在前台时,这非常有效,但是当我的应用程序在后台或关闭时,我得到一个 ImplementationError,因为 MainActivity 不存在。

我现在的问题是,当应用程序在后台或关闭时,如何调用方法通道? 我读到我需要为此创建一个服务,但我不知道如何从服务内部设置方法调用处理程序。 谁能帮我解决这个问题?

编辑:

我尝试在第一次打开应用程序后通过在 dart 主文件中进行方法调用来启动服务

override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) 
    super.configureFlutterEngine(flutterEngine)
    GeneratedPluginRegistrant.registerWith(flutterEngine)
    MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler  call, result ->
        if (call.method == "startHeadlessService")
            if (!context.isMyServiceRunning(FlutterService::class.java)) 
                Log.d("pushNotificationService", "Service starting")
                val intent = Intent(context, FlutterService::class.java)
                context.startService(intent)
             else 
                Log.d("pushNotificationService", "Service already Running")
            
            result.success(null)
         else 
            result.notImplemented()
        
    

在我的服务的 onStartCommand 函数中,我尝试设置一个方法通道,如下所示:

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int 
    Log.d("TAG", "Hello foreground service")
    FlutterMain.startInitialization(this);
    FlutterMain.ensureInitializationComplete(this, null);
    val flutterEngine = FlutterEngine(this)
    GeneratedPluginRegistrant.registerWith(flutterEngine)
    MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler  call, result ->
        if (call.method == "createConversationShortcut") 
            val personName = call.argument<String>("personName")
            val personIcon = call.argument<String>("personIcon")
            if (personName != null && personIcon != null) 
                val shortcutId = createConversationShortcut(personName, personIcon)
                result.success(shortcutId)
             else 
                result.error("argument-error", "Too few arguments", null)
            
        
    
    return super.onStartCommand(intent, flags, startId)

但我仍然收到错误:

Unhandled Exception: MissingPluginException(No implementation found for method saveBitmapAsCircle on channel ...)

【问题讨论】:

flutter-run-an-app-as-a-background-service 为您提供后台服务。 我已经看过所有这些了,但这并没有真正帮助我解决我的问题,因为我不擅长 android 应用程序开发。当应用不在前台时收到通知时,我只需要一种方法来调用本机方法来创建快捷方式 【参考方案1】:

我不知道如何在没有活动活动的情况下使方法通道可用,但我发现如果您创建一个插件,即使应用程序关闭,方法通道也始终可以访问,所以我制作了一个插件可用于创建对话快捷方式作为flutter_local_notifications 插件的补充。

如果有人感兴趣: android_conversation_shortcut

【讨论】:

【参考方案2】:

如果你想流畅运行,请使用这个包:https://pub.dev/packages/background_fetch

【讨论】:

以上是关于Flutter:在应用程序关闭或在后台运行原生 android 代码的主要内容,如果未能解决你的问题,请参考以下文章

如何在后台使用 Flutter Method Channel(应用最小化/关闭)

GCM BroadcastReceiver 仅在应用程序正在运行或在后台运行时触发

Flutter Firebase前台推送通知未显示但后台正在运行

Firebase 消息传递 - 当应用程序关闭或在 Flutter 中休眠时不会发送通知

Flutter Firebase Cloud Messaging - 应用程序在后台时的通知

如何检测flutter网站是不是在浏览器后台运行?