如何在Flutter插件中使用接收器(BroadcastReceiver)?

6

问题

我正在开发一个需要使用 Radar.io SDK 功能的应用程序,因此我决定创建一个插件,以便在项目完成后与 Flutter 社区分享插件。问题是,我无法在插件中接收事件。这对于插件非常重要,因为为了接收诸如地理位置触发器和其他后台事件等事件,该接收器需要接收实时信息。

我已经成功实现了他们在 Android 上 SDK 的每个功能(使用 Kotlin),可以在此处查看https://radar.io/documentation/sdk,但是当事件发生时,接收器没有被调用。我知道设备正在被跟踪,因为位置在 Radar.io 仪表板上更新,但是当各种事件发生时根本不执行接收器。

我尝试过的方法

以下是文档告诉您如何在清单中注册接收器的方法。

  <receiver
      android:name=".MyRadarReceiver"
      android:enabled="true"
      android:exported="false">
      <intent-filter>
          <action android:name="io.radar.sdk.RECEIVED" />
      </intent-filter>
  </receiver>

这适用于标准应用程序,但是当我在清单中注册接收器时,应用程序不会查找接收器所在的插件目录。相反,它会查找应用程序的Android目录,并返回无法找到数据路径的错误。这会导致应用程序终止。
为了解决这个问题,我发现可以以编程方式设置接收器,所以我决定尝试一下。
在插件的主要Kotlin文件中,我编写了以下代码行,以在registerWithonAttachedToEngine中执行。我的理解是,这些基本上是onStart函数。registerWith 是为与旧设备兼容而保留的较旧函数。
val myRadarReceiver = MyRadarReceiver()
val intentFilter = IntentFilter()
intentFilter.addAction("io.radar.sdk.RECEIVED")
ContextWrapper(this.context).registerReceiver(myRadarReceiver, intentFilter)

被引用为MyRadarReceiver()的类是SDK中RadarReceiver类的扩展,扩展了BroadcastReceiver,这意味着这是我自己的广播接收器。以下是该类:

public class MyRadarReceiver: RadarReceiver() {

  override fun onEventsReceived(context: Context, events: Array<RadarEvent>, user: RadarUser) {
    println("test: " + "an event was received")
  }

  override fun onLocationUpdated(context: Context, location: Location, user: RadarUser) {
    println("test: " + "location was updated")
  }

  override fun onClientLocationUpdated(context: Context, location: Location, stopped: Boolean, source: Radar.RadarLocationSource) {
    println("test: " + "client location updated")
  }

  override fun onError(context: Context, status: Radar.RadarStatus) {
    println("test: " + status)
  }

  override fun onLog(context: Context, message: String) {
    println("test: " + message)
  }

}

当我在模拟设备上启用后台跟踪时,我期望在MyRadarReceiver中的某个函数将被执行,但终端没有输出任何内容。没有错误,但也没有收到任何信息。这让我怀疑是否我设置了不正确的意图,或者接收器编写有误。我不知道是否有其他设置意图的方法,所以我决定像下面这样以更简单的形式重新编写MyRadarReceiver:

public class MyRadarReceiver: BroadcastReceiver() {
  override fun onReceive(context: Context, intent: Intent) {
    println("works! Something was received")
  }
}

不幸的是,控制台没有输出任何内容,因此我现在认为这与我的意图有关。

总结

我剩下的唯一解决方案是在应用程序中编写接收器,而不是在插件中编写。这可能有效,但我希望在完成后与Flutter社区分享此插件。雷达是一个非常强大的平台,将此插件带给Flutter社区可能会产生巨大的影响。


你插件的清单标签中的 package 属性是什么? - Nitrodon
对于插件,它的package="agency.sparc.flutter_radar_io",但对于示例应用程序,则为package="agency.sparc.flutter_radar_io_example" - Anthony Sette
有没有办法让我从插件包中引用类?因为如果可以的话,我可以将接收器添加到应用程序XML文件中,然后插件就会接收到它。或者也许我可以在插件XML中添加一些内容,但是当我这样做时,我会得到一个覆盖应用程序Android标签的错误... Attribute application@label value=(flutter_radar_io_example) from AndroidManifest.xml:16:9-49 is also present at [:flutter_radar_io] AndroidManifest.xml:9:9-41 - Anthony Sette
2个回答

6

Android基础知识

一个有用的资源是广播概述

这取决于您想在运行时注册广播接收器还是在清单文件中静态声明。阅读动态注册与静态注册的BroadcastReceiver。如果您想要一个意图来启动您的接收器,它必须被静态声明。如果您想要仅在您的应用已经运行时进行监听,则在运行时注册它。

这都是正常的Android操作。


Flutter特定内容

在广播接收器中,不能保证Flutter应用程序、Flutter引擎或DartVM正在运行。如果想要运行Flutter,需要生成一个隔离环境。

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

然后,您可以通过调用以下方式通知插件/ Flutter 应用程序:

// Even though lifecycle parameter is @NonNull, the implementation `FlutterEngineConnectionRegistry`
// does not use it, because it is a bug in the API design. See https://github.com/flutter/flutter/issues/90316
flutterEngine.getBroadcastReceiverControlSurface().attachToBroadcastReceiver(this, null); // This is the class you are in, the broadcast receiver

当你即将完成广播接收器的执行时:

flutterEngine.getBroadcastReceiverControlSurface().detachFromBroadcastReceiver();

这将确保在广播接收器附加和分离时,BroadcastReceiverAware插件能够得到通知。


这看起来很有前途,但是如果调用backgroundMethodChannel.invokeMethod,它会无处着陆。在Flutter/Dart端应该怎么处理这个调用呢? - mspnr
我觉得你误解了问题,这个问题是关于在Android Flutter插件中监听BroadcastReceiver,而不是在Dart/Flutter应用程序中使用MethodChannel - Ben Butterworth
好的。我的兴趣可能与主要问题没有直接关联,但我觉得你的建议非常有用。你能否看一下我的问题https://stackoverflow.com/questions/76496662/how-to-start-flutter-processing-from-android-broadcastreceiver?我想你可以说一下是否可能。 - mspnr

1
在看到Nitrodon的评论后,我从不同角度看待了这个问题,并找到了解决方案。我不确定为什么程序化的接收器初始化没有起作用,但是,这是我在我的插件Android清单中所写的内容。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.youorganization.yourproject">
  <application
    android:name="io.flutter.app.FlutterApplication">
    <receiver
      android:name=".MyRadarReceiver"
      android:enabled="true"
      android:exported="false">
        <intent-filter>
            <action android:name="io.radar.sdk.RECEIVED" />
        </intent-filter>
    </receiver>
  </application>
</manifest>

您不能在应用程序标签中添加Android标签或其他内容,否则它会因与应用程序的Android清单文件冲突而抛出错误。因此,您只需要在简单的应用程序标签中声明接收器,该标签仅包含名称io.flutter.app.FlutterApplication

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接