美文网首页Flutter
Flutter插件学习之Native通信详解

Flutter插件学习之Native通信详解

作者: RedLi | 来源:发表于2020-04-28 11:48 被阅读0次

    前言

    我们都知道Flutter开发的app是可以同时在iOS和Android系统上运行的。显然Flutter需要有和Native通信的能力。在实际项目中,Flutter并不能全面满足项目需求,比如获取一些硬件信息(如电池电量),这些都是一些比较简单的Native需求,Flutter官方也给出了一些比较常用的Plugin。但在实际项目中可能需求就没那么简单了,比如融云通讯、环信通讯,再或者项目自定义的需求,这可能就需要我们自己去写插件了。这里我就以Flutter获取Native电池电量和电池状态(充电中、充满电、未充电)为例(包括IOS-Swift 和 Android-Kotlin)来实现自己的需求。

    思路

    Flutter是如何做到的呢?当然是Flutter官方给的Platform Channels。如图:

    image

    上图来自Flutter官网,表明了Platform Channels的架构示意图。Platform Channel包括MethodChannel、EventChannel和BasicMessageChannel三大通道。

    Platform Channel 支持的数据类型

    比较常用

    Dart iOS-Swift Android-Kotlin
    null nil null
    bool Bool Boolean
    int Int Int
    float Int Int
    double Double Double
    String String String
    List Array List
    Map Dictionary HashMap

    MethodChannel

    俗称方法通道(个人见解)用于传递方法调用

    以 Flutter 获取 手机电量为例, 在 Flutter 界面中要想获取 Android/iOS 的电量, 首先要在 Native 编写获取电量的功能, 供 Flutter 来调用。

    • Native - iOS
    public class SwiftFlutterPluginLearningPlugin: NSObject, FlutterPlugin {
        
        public static func register(with registrar: FlutterPluginRegistrar) {
            let instance = SwiftFlutterPluginLearningPlugin()
            let methodChannel = FlutterMethodChannel(name: "plugins.limit.io/battery", binaryMessenger: registrar.messenger())
            //注册电池(plugins.limit.io/battery)方法通道
            registrar.addMethodCallDelegate(instance, channel: methodChannel)
        }
    
        public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
            switch call.method {
            case "getBatteryLevel":
                let batterLevel = getBatteryLevel()
                if(batterLevel == -1) {
                    result(FlutterError(code: "UNAVAILABLE", message: "Battery info unavailable", details: nil));
                } else {
                    result(batterLevel)
                }
            default:
                result(FlutterMethodNotImplemented)
            }
        }
        
        //获取电池电量
        private func getBatteryLevel() -> Float {
            let device = UIDevice.current
            device.isBatteryMonitoringEnabled = true
            if device.batteryState == .unknown {
                return -1;
            } else {
                return UIDevice.current.batteryLevel
            }
        }
    }
    
    
    • Native - Android
    public class FlutterPluginLearningPlugin : FlutterPlugin, MethodCallHandler, EventChannel.StreamHandler {
    
        private var applicationContext: Context? = null
    
        /**
         * 方法通道
         */
        private var methodChannel: MethodChannel? = null
    
        /**
         * 连接到引擎
         */
        override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
            onAttachedToEngine(flutterPluginBinding.applicationContext, flutterPluginBinding.binaryMessenger)
        }
    
        private fun onAttachedToEngine(applicationContext: Context, messenger: BinaryMessenger) {
            this.applicationContext = applicationContext
            methodChannel = MethodChannel(messenger, "plugins.limit.io/battery")
            methodChannel?.setMethodCallHandler(this)
        }
    
        companion object {
            @JvmStatic
            fun registerWith(registrar: Registrar) {
                val instance = FlutterPluginLearningPlugin()
                instance.onAttachedToEngine(registrar.context(), registrar.messenger())
            }
        }
    
        /**
         * 回调方法
         */
        override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
            if (call.method == "getBatteryLevel") {
                val batterLevel = getBatteryLevel()
                if (batterLevel != -1) {
                  result.success(batterLevel)
                } else {
                  result.error("UNAVAILABLE", "Battery level not available.", null);
                }
            } else {
                result.notImplemented()
            }
        }
    
        /**
         * 从引擎中脱离
         */
        override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
            applicationContext = null
            methodChannel?.setMethodCallHandler(null)
            methodChannel = null
        }
    
        /**
         * 获取电池电量方法
         */
        private fun getBatteryLevel(): Int {
            var batteryLevel = -1
            batteryLevel = if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
                val batteryManager = applicationContext?.let { getSystemService(it, BatteryManager::class.java) }
                batteryManager!!.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
            } else {
                val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
                intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
            }
    
            return batteryLevel
        }
    }
    
    • Flutter

    flutter 插件

    class FlutterPluginLearning {
      factory FlutterPluginLearning() {
        if (_instance == null) {
          final MethodChannel methodChannel =
              const MethodChannel('plugins.limit.io/battery');
          _instance = FlutterPluginLearning.init(methodChannel);
        }
        return _instance;
      }
    
      FlutterPluginLearning.init(this._methodChannel);
    
      static FlutterPluginLearning _instance;
    
      final MethodChannel _methodChannel;
    
      Future<int> get batteryLevel => _methodChannel
          .invokeMethod<int>('getBatteryLevel')
          .then<int>((dynamic result) => result);
    }
    
    

    flutter 调用

    FlutterPluginLearning _battery = FlutterPluginLearning();
    _battery.batteryLevel.then((int batteryLevel) {
          //to do something you want
    });
    

    EventChannel

    俗称流通道(个人见解)用于数据流(event streams)的通信
    以 Flutter 获取 手机电池状态为例, 在 Flutter 界面中要想获取 Android/iOS 的电池状态, 首先要在 Native 编写获取电池状态的功能, 供 Flutter 来调用。

    • Native - iOS
    public class SwiftFlutterPluginLearningPlugin: NSObject, FlutterPlugin, FlutterStreamHandler {
        
        var eventSink: FlutterEventSink?
        
        public static func register(with registrar: FlutterPluginRegistrar) {
            let instance = SwiftFlutterPluginLearningPlugin()
            let evenChannel = FlutterEventChannel(name: "plugins.limit.io/charging", binaryMessenger: registrar.messenger())
            evenChannel.setStreamHandler(instance)
        }
    
        public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
            
        }
        
        public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
            eventSink = events
            UIDevice.current.isBatteryMonitoringEnabled = true
            self.sendBatteryStateEvent()
            NotificationCenter.default.addObserver(self, selector: #selector(onBatteryStateDidChange(notification:)), name: UIDevice.batteryStateDidChangeNotification, object: nil)
            return nil
        }
        
        public func onCancel(withArguments arguments: Any?) -> FlutterError? {
            NotificationCenter.default.removeObserver(self)
            eventSink = nil
            return nil
        }
        
        @objc private func onBatteryStateDidChange(notification: NSNotification?) {
            self.sendBatteryStateEvent()
        }
        
        private func sendBatteryStateEvent() {
            if eventSink == nil {
                return
            }
            let state = UIDevice.current.batteryState
            switch state {
            case .full:
                eventSink!("full")
                break
            case .charging:
                eventSink!("charging")
                break
            case .unplugged:
                eventSink!("unplugged")
                break
            default:
                eventSink!(FlutterError(code: "UNAVAILABLE", message: "Charging status unavailable", details: nil))
                break
            }
        }
    }
    
    
    • Native - Android
    public class FlutterPluginLearningPlugin : FlutterPlugin, MethodCallHandler, EventChannel.StreamHandler {
    
        private var applicationContext: Context? = null
    
        /**
         * 事件流通道
         * Native 需要频繁的发送消息给 Flutter, 比如监听网络状态, 蓝牙设备等等然后发送给 Flutter
         */
        private var eventChannel: EventChannel?= null
    
        private var chargingStateChangeReceiver: BroadcastReceiver? = null
    
        /**
         * 连接到引擎
         */
        override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
            onAttachedToEngine(flutterPluginBinding.applicationContext, flutterPluginBinding.binaryMessenger)
        }
    
        private fun onAttachedToEngine(applicationContext: Context, messenger: BinaryMessenger) {
            this.applicationContext = applicationContext
            eventChannel = EventChannel(messenger, "plugins.limit.io/charging")
            eventChannel?.setStreamHandler(this)
        }
    
        companion object {
            @JvmStatic
            fun registerWith(registrar: Registrar) {
                val instance = FlutterPluginLearningPlugin()
                instance.onAttachedToEngine(registrar.context(), registrar.messenger())
            }
        }
    
        /**
         * 回调方法
         */
        override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
            
        }
    
        /**
         * 从引擎中脱离
         */
        override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
            applicationContext = null
            eventChannel?.setStreamHandler(null)
            eventChannel = null
        }
    
        /**
         * 监听
         */
        override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
            chargingStateChangeReceiver = createChargingStateChangeReceiver(events)
            applicationContext?.registerReceiver(chargingStateChangeReceiver, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
        }
    
        /**
         * 取消监听
         */
        override fun onCancel(arguments: Any?) {
            applicationContext?.unregisterReceiver(chargingStateChangeReceiver)
            chargingStateChangeReceiver = null
        }
    
        private fun createChargingStateChangeReceiver(events: EventChannel.EventSink?): BroadcastReceiver {
            return object : BroadcastReceiver() {
                override fun onReceive(context: Context?, intent: Intent?) {
                    when (intent?.getIntExtra(BatteryManager.EXTRA_STATUS, -1)) {
                        BatteryManager.BATTERY_STATUS_CHARGING -> events?.success("charging")
                        BatteryManager.BATTERY_STATUS_FULL -> events?.success("full")
                        BatteryManager.BATTERY_STATUS_DISCHARGING -> events?.success("discharging")
                        else -> events?.error("UNAVAILABLE", "Charging status unavailable", null)
                    }
                }
            }
        }
    
    }
    
    
    • Flutter

    flutter 插件

    enum BatteryState {
      /// The battery is completely full of energy.
      full,
    
      /// The battery is currently storing energy.
      charging,
    
      /// The battery is currently losing energy.
      discharging
    }
    
    class FlutterPluginLearning {
      factory FlutterPluginLearning() {
        if (_instance == null) {
          
          final EventChannel eventChannel =
              const EventChannel('plugins.limit.io/charging');
          _instance = FlutterPluginLearning.init(eventChannel);
        }
        return _instance;
      }
    
      FlutterPluginLearning.init(this._eventChannel);
    
      static FlutterPluginLearning _instance;
    
      final EventChannel _eventChannel;
      Stream<BatteryState> _onBatteryStateChanged;
    
      Stream<BatteryState> get onBatteryStateChanged {
        if (_onBatteryStateChanged == null) {
          _onBatteryStateChanged = _eventChannel
              .receiveBroadcastStream()
              .map((dynamic event) => _parseBatteryState(event));
        }
        return _onBatteryStateChanged;
      }
    
      BatteryState _parseBatteryState(String state) {
        switch (state) {
          case 'full':
            return BatteryState.full;
          case 'charging':
            return BatteryState.charging;
          case 'discharging':
            return BatteryState.discharging;
          default:
            throw ArgumentError('$state is not a valid BatteryState.');
        }
      }
    }
    

    flutter 调用

    FlutterPluginLearning _battery = FlutterPluginLearning();
    StreamSubscription<BatteryState> _batteryStateSubscription;
    _batteryStateSubscription =
            _battery.onBatteryStateChanged.listen((BatteryState state) {
          String batteryState = 'UnKnow';
          switch (state) {
            case BatteryState.full:
              batteryState = '已充满';
              break;
            case BatteryState.charging:
              batteryState = '充电中';
              break;
            case BatteryState.discharging:
              batteryState = '未充电';
              break;
            default:
          }
        // do something you want
        });
    

    BasicMessageChannel

    俗称消息直接通道(个人见解)用于传递字符串和半结构化的信息。
    如果仅仅是简单的通信而不是调用某个方法或者是事件流, 可以使用 BasicMessageChannel。BasicMessageChannel 也可以实现 Flutter 和 Native 的双向通信, 下面的示例图就是官方的例子:

    image

    详细查看(八)Flutter 和 Native之间的通信详解 的 BasicMessageChannel

    Reference

    (八)Flutter 和 Native之间的通信详解

    官网 - platform-channels

    源码

    https://github.com/TBoyLi/flutter_plugin_learning 觉得ok! Star ✨✨✨✨✨✨

    相关文章

      网友评论

        本文标题:Flutter插件学习之Native通信详解

        本文链接:https://www.haomeiwen.com/subject/zflvwhtx.html