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

How do you use receiver (BroadcastReceiver) in a flutter plugin?

问题

我正在构建一个需要 Radar.io SDK 的一些功能的应用程序,所以我决定创建插件,以便在这个项目结束后我可以与 Flutter 社区的其他人共享插件.问题是,我无法在此插件中接收事件。这对插件很重要,因为为了接收诸如地理定位触发器和其他背景事件之类的事件,此接收器需要接收实时信息。

我已经成功地在他们的 SDK 中为 Android(使用 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 目录和 returns 中查找找不到数据路径的错误。这会导致应用程序终止。

为了解决这个问题,我发现可以通过编程方式设置接收器,所以我决定试一试。

在插件的主 Kotlin 文件中,我编写了以下要在 registerWithonAttachedToEngine 中执行的行。据我了解,这些基本上是 onStart 功能。 registerWith 是为了与旧设备兼容而保留的旧功能。

val myRadarReceiver = MyRadarReceiver()
val intentFilter = IntentFilter()
intentFilter.addAction("io.radar.sdk.RECEIVED")
ContextWrapper(this.context).registerReceiver(myRadarReceiver, intentFilter)

引用为 MyRadarReceiver() 的 class 是 SDK 中 RadarReceiver class 的扩展,它扩展了 BroadcastReceiver 意味着这是我的广播接收器。这是 class:

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 中的功能之一能够执行,但终端没有打印任何内容。没有错误,但是什么也没有收到。这让我想知道我是否错误地设置了意图,或者 Receiver 是否写得不正确。我不知道另一种设置意图的方法,所以我决定以更简单的形式重写 MyRadarReceiver,如下所示:

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

不幸的是,控制台上没有打印任何内容,所以我现在相信这与我的意图有关。

总结

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

解决方案

在看到 Nitrodon 的评论后,我对问题有了不同的看法并找到了解决方案。我不确定为什么程序化的 Receiver 初始化不起作用,但是,这是我在 插件的 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 的名称。我希望这可以帮助别人!如果有人有什么要补充的,请补充,但它终于起作用了!

Android 基础知识

有用的资源是 Broadcasts overview

这取决于您是想在 运行 时注册您的广播接收器,还是在清单中静态声明它。阅读 Dynamic Registration vs Static Registration of BroadcastReceiver。如果您想要启动接收器的意图,则必须静态声明它。如果您希望它仅在您的应用已经 运行ning 时收听,请在 运行 时间注册它。

这都是正常的 Android 东西。


Flutter 细节

在广播接收器中,您无法保证 Flutter 应用程序、Flutter 引擎或 DartVM 运行正在运行。如果你想 运行 Flutter,你需要生成一个 Isolate。

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 插件在附加和分离广播接收器时得到通知。