Flutter与原生通信:Platform Channel (MethodChannel、EventChannel)以及遇到的问题

一、架构概述:平台通道

消息使用平台通道在客户端(UI)和宿主(平台)之间传递,如下图所示:

消息和响应以异步的形式进行传递,以确保用户界面能够保持响应。

简单介绍Platfrom Channel的三个API

MethodChannel : Flutter与原生方法相互调用,用于方法掉用。

EventChannel : 原生发送消息,Flutter接收,用于数据流通信

BasicMessageChannel : Flutter与原生相互发送消息,用于数据传递

三种Channel之间互相独立,各有用途,但它们在设计上却非常相近。每种Channel均有三个重要成员变量:

  • name: String类型,代表Channel的名字,也是其唯一标识符。
  • messager:BinaryMessenger类型,代表消息信使,是消息的发送与接收的工具。
  • codec: MessageCodec类型或MethodCodec类型,代表消息的编解码器。
  • 例:MethodChannel(BinaryMessenger messenger, String name, MethodCodec codec)

注:一个Flutter应用中可能存在多个Channel,每个Channel在创建时必须指定一个独一无二的name,Channel之间使用name来区分彼此。当有消息从Flutter端发送到Platform端时,会根据其传递过来的channel name找到该Channel对应的Handler(消息处理器)。

下面我们通过一个获取电池电量的插件来介绍一下MethodChannel 与 EventChannel ,该插件中我们在Dart中通过getBatteryLevel 调用Android BatteryManager API

二、MethodChannel 与 EventChannel 官方示例

首先创建一个新的应用:

  • 在终端中运行:flutter create batterylevel

默认情况下,我们的模板使用 Kotlin 编写 Android 或使用 Swift 编写 iOS 代码。要使用 Java 或 Objective-C,请使用 -i 和/或 -a 标志:

  • 在终端中运行:flutter create -a kotlin batterylevel / flutter create -a java batterylevel

1、MethodChannel 基本流程

MethodChannel 是双向的,此处以flutter调用native为例,native调用flutter是一样的只是角色互调

  1. [native端] 使用MethodChannel.setMethodCallHandler注册回调
  2. [flutter端] 使用MethodChannel.invokeMethod发起异步调用,调用原生里的方法,等待返回值
  3. [native端] 通过Result.success返回值

2、EventChannel基本流程

  1. [native端] 使用EventChannel.setStreamHandler注册
  2. [native端] EventChannel初始完成后,在StreamHandler的onListen回调中创建和注册广播
  3. [flutter端] 通过EventChannel.receiveBroadcastStream注册监听,实时监听返回值。
  4. [native端] 使用EventSink.success发送消息
  5. [flutter端] 接受值

Flutter端代码

class _MyHomePageState extends State<MyHomePage> {
  //一个应用中所使用的所有通道名称必须是唯一的,并且两端使用的名称要相同
  static const MethodChannel methodChannel =
      MethodChannel('samples.flutter.io/battery');
  static const EventChannel eventChannel =
      EventChannel('samples.flutter.io/charging');

  String _batteryLevel = 'Battery level: unknown.';
  String _chargingStatus = 'Battery status: unknown.';

  Future<void> _getBatteryLevel() async {
    String batteryLevel;
    try {
      //MethodChannel-2.[flutter端] 使用MethodChannel.invokeMethod发起异步调用,调用原生里的方法,等待返回值
      final int result = await methodChannel.invokeMethod('getBatteryLevel');
      batteryLevel = 'Battery level: $result%.';
    } on PlatformException {
      batteryLevel = 'Failed to get battery level.';
    }
    setState(() {
      _batteryLevel = batteryLevel;
    });
  }

  @override
  void initState() {
    super.initState();
    //EventChannel-3.[flutter端] 通过EventChannel.receiveBroadcastStream注册监听,实时监听返回值。
    eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError);
  }
  void _onEvent(Object event) {
    setState(() {
      _chargingStatus = event;

    });
  }
  void _onError(Object error) {
    setState(() {
      _chargingStatus = 'error';
    });
  }
  @override
  Widget build(BuildContext context) {
    return Material(
        child: Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: <Widget>[
          Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text(_batteryLevel, key: const Key('Battery level label')),
              Padding(
                padding: const EdgeInsets.all(16.0),
                child: RaisedButton(
                  child: const Text('Refresh'),
                  onPressed: _getBatteryLevel,
                ),
              ),
            ],
          ),
          Text(_chargingStatus),
        ],
      ),
    ));
  }
}

Android端代码

Java:

package com.example.platformchannel;

import android.content.BroadcastReceiver;
import android.content.Context;
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 androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.EventChannel.EventSink;
import io.flutter.plugin.common.EventChannel.StreamHandler;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugins.GeneratedPluginRegistrant;

public class MainActivity extends FlutterActivity {
  //两端使用的通道名称要相同
  private static final String BATTERY_CHANNEL = "samples.flutter.io/battery";
  private static final String CHARGING_CHANNEL = "samples.flutter.io/charging";

  @Override
  public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
    //EventChannel-1.[native端] 使用EventChannel.setStreamHandler注册
    new EventChannel(flutterEngine.getDartExecutor(), CHARGING_CHANNEL).setStreamHandler(
      new StreamHandler() {
        private BroadcastReceiver chargingStateChangeReceiver;
        @Override
        public void onListen(Object arguments, EventSink events) {
          //EventChannel-2.[native端] EventChannel初始完成后,在StreamHandler的onListen回调中创建和注册广播
          chargingStateChangeReceiver = createChargingStateChangeReceiver(events);//创建
          registerReceiver(//注册
              chargingStateChangeReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
        }
        @Override
        public void onCancel(Object arguments) {
          unregisterReceiver(chargingStateChangeReceiver);
          chargingStateChangeReceiver = null;
        }
      }
    );
    //MethodChannel-1.[native端] 使用MethodChannel.setMethodCallHandler注册回调
    new MethodChannel(flutterEngine.getDartExecutor(), BATTERY_CHANNEL).setMethodCallHandler(
      new MethodCallHandler() {
        @Override
        public void onMethodCall(MethodCall call, Result result) {
          if (call.method.equals("getBatteryLevel")) {
            int batteryLevel = getBatteryLevel();
            if (batteryLevel != -1) {
              //MethodChannel-3.[native端] 通过Result.success返回值
              result.success(batteryLevel);
            } else {
              result.error("UNAVAILABLE", "Battery level not available.", null);
            }
          } else {
            result.notImplemented();
          }
        }
      }
    );
  }

  private BroadcastReceiver createChargingStateChangeReceiver(final EventSink events) {
    return new BroadcastReceiver() {
      @Override
      public void onReceive(Context context, Intent intent) {
        int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);

        if (status == BatteryManager.BATTERY_STATUS_UNKNOWN) {
          events.error("UNAVAILABLE", "Charging status unavailable", null);
        } else {
          boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
                               status == BatteryManager.BATTERY_STATUS_FULL;
          //EventChannel-4.[native端] 使用EventSink.success发送消息
          events.success(isCharging ? "charging" : "discharging");
        }
      }
    };
  }

  private int getBatteryLevel() {
    if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
      BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);
      return batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
    } else {
      Intent intent = new ContextWrapper(getApplicationContext()).
          registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
      return (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) /
          intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
    }
  }
}

Kotlin:Java代码复制AndroidStudio的kotlin文件里会自动提示转成kotlin 此处省略。

 

MethodChannel.setMethodCallHandler、EventChannel.setStreamHandler也可以封装到外面

像这样开发Flutter 的 Android 插件有个注意的地方:

自 1.12 版本发布后, Android 平台已可以使用新的 Android 插件 API 。基于 PluginRegistry.Registrar 的 API 不会立刻废弃,但我们鼓励您向基于 FlutterPlugin 的 API 进行迁移。

此次涉及最大的调整,应该是 Android 插件的改进 Android plugins APIs 的相关变化,该调整需要用户重新调整 Flutter 项目中 Android 模块和插件的代码进行适配。

新旧版本差异请看这里https://github.com/flutter/flutter/wiki/Upgrading-pre-1.12-Android-projects

我在flutter与native原生通信开发完后出现了这样的错误:

[ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: MissingPluginException(No implementation found for method getAll on channel plugins.flutter.io/shared_preferences)

解决办法:https://github.com/flutter/flutter/issues/10912

我解决问题是通过在public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine)方法里加上了GeneratedPluginRegistrant.registerWith(flutterEngine);

-import io.flutter.app.FlutterActivity;
-import io.flutter.plugin.common.MethodCall;
+import androidx.annotation.NonNull;
+import io.flutter.embedding.android.FlutterActivity;
+import io.flutter.embedding.engine.FlutterEngine;
 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 = "samples.flutter.dev/battery";
-    //以前的注册方法
-    @Override
-    public 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) {
-                        // Your existing code
-                    }
-                });
-    }
+    //现在的注册方法
+    @Override
+    public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
+        GeneratedPluginRegistrant.registerWith(flutterEngine);//我忘了这句
+        new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
+                .setMethodCallHandler(
+                    (call, result) -> {
+                        // Your existing code
+                }
+        );
+    }
 }

觉得文章不错的话,点个赞把,比心~

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Flutter 是一个跨平台的移动应用开发框架,它可以让开发者使用一套代码同时开发 iOS 和 Android 应用。而在某些情况下,我们可能需要集成原生 SDK 实现一些功能。本文将介绍如何在 Flutter 中集成原生 SDK。 首先,我们需要在 Flutter 中创建一个平台通道(Platform Channel)来实现 Flutter原生代码的通信。 1.创建一个平台通道 在 Flutter 中,平台通道定义了 Dart 代码和原生代码之间的通信方式。我们可以通过 MethodChannelEventChannel 或 BasicMessageChannel 等方式来创建平台通道。 以 MethodChannel 为例,我们可以在 Flutter 中创建一个 MethodChannel: ``` final MethodChannel platformChannel = MethodChannel('com.example.platform_channel'); ``` 这里的 com.example.platform_channel 是一个字符串,用来标识 Flutter原生代码之间的通道。 2.在原生代码中实现方法 在原生代码中,我们需要实现与 Flutter 中定义的 MethodChannel 对应的方法。例如,我们可以在 Android 中创建一个名为 MyPlugin 的类来实现这个方法: ``` public class MyPlugin implements MethodCallHandler { private static final String CHANNEL = "com.example.platform_channel"; public static void registerWith(Registrar registrar) { final MethodChannel channel = new MethodChannel(registrar.messenger(), CHANNEL); channel.setMethodCallHandler(new MyPlugin()); } @Override public void onMethodCall(MethodCall call, Result result) { if (call.method.equals("getDeviceInfo")) { String deviceInfo = getDeviceInfo(); result.success(deviceInfo); } else { result.notImplemented(); } } private String getDeviceInfo() { // 获取设备信息的代码 return "device info"; } } ``` 这里的 getDeviceInfo 方法用来获取设备信息,并将结果返回给 Flutter。 3.在 Flutter 中调用方法 在 Flutter 中,我们可以通过 MethodChannel 来调用 MyPlugin 中实现的方法: ``` String deviceInfo = await platformChannel.invokeMethod('getDeviceInfo'); ``` 这里的 invokeMethod 方法用来调用 getDeviceInfo 方法,并将结果返回给 Flutter。 以上就是在 Flutter 中集成原生 SDK 的基本流程。在实际使用中,我们还需要注意一些细节,例如方法参数和返回值的类型转换等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值