美文网首页
Platform Channel (iOS)

Platform Channel (iOS)

作者: 烟影很美 | 来源:发表于2022-08-03 18:01 被阅读0次

    1. 基本使用

    Basic Message Channel

      final BasicMessageChannel _bmc = const BasicMessageChannel('basic_message', StringCodec());
      String _string = 'null';
      _requestMesssage() async {
        // 这种方式会通过原生端调用 `callback` 返回消息, 不会触发 `setMessageHandler`
        _string = await _bmc.send('i need a string');
        setState(() {});
      }
    
      _handleMessage() {
        // 原生端通过`bmc.send()`触发
        _bmc.setMessageHandler((message) async {
          setState(() {
            if (message is String) {
              _string = message;
            }
          });
          return 'i\' dart side, i receive a message: $message';
        });
      }
    
      @override
      void initState() {
        super.initState();
        _handleMessage();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('Basic Message Channel'),
          ),
          body: child: Text(_string),
          floatingActionButton: ElevatedButton(
            onPressed: () {
              _requestMesssage();
            },
            child: const Text('request message'),
          ),
        );
      }
    

    Method Channel

      final MethodChannel _mc = const MethodChannel('method_channel');
      bool _changed = false;
      String _message = 'null';
      _invokeMethod() async {
        _message = await _mc.invokeMethod('getString', 1321);
        setState(() {});
      }
    
      _handleMethod() {
        _mc.setMethodCallHandler((call) async {
          switch (call.method) {
            case 'changeColor':
              setState(() {
                _changed = !_changed;
                print(call.arguments);
              });
              break;
            default:
          }
        });
      }
    
      @override
      void initState() {
        super.initState();
        _handleMethod();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('Method Channle'),
          ),
          body: Container(
                  width: 200,
                  height: 100,
                  color: _changed ? Colors.red : Colors.blue,
                  child: Text(_message),
                ),
          floatingActionButton: ElevatedButton(
              onPressed: () {
                _invokeMethod();
              },
              child: const Text('invoke method')),
        );
      }
    

    Event Channel

      final EventChannel _ec = const EventChannel('event_channel');
      int _num = 0;
    
      _onEvent(event) {
        if (event is int) {
          print(event);
          setState(() {
            _num = event;
          });
        }
      }
    
      late Stream _stream;
      @override
      void initState() {
        super.initState();
        _ec.receiveBroadcastStream().listen(_onEvent);
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('Method Channle'),
          ),
          body: Container(
                  width: 200,
                  height: 100,
                  color: Colors.blue,
                  child: Text('$_num'),
                ),
          floatingActionButton: ElevatedButton(
              onPressed: () {
                _stream.listen(_onEvent);
              },
              child: const Text('invoke method')),
        );
      }
    

    iOS代码

    #import "AppDelegate.h"
    #import "GeneratedPluginRegistrant.h"
    
    @interface EventHander : NSObject<FlutterStreamHandler>
    @property (nonatomic, strong, nullable)FlutterEventSink events;
    @property (nonatomic, assign)int count;
    @end
    
    @implementation EventHander
    
    - (instancetype)init {
        NSLog(@"event hander init");
        if (self = [super init]) {
            [self startTimer];
        }
        return self;
    }
    
    - (void)startTimer {
        [NSTimer scheduledTimerWithTimeInterval:2 repeats:true block:^(NSTimer * _Nonnull timer) {
            self.count++;
            if (self.events != nil) {
                self.events([[NSNumber alloc] initWithInt:self.count]);
            }
        }];
    }
    
    - (FlutterError * _Nullable)onCancelWithArguments:(id _Nullable)arguments {
        NSLog(@"%@", arguments);
        self.events = nil;
        return nil;
    }
    
    - (FlutterError * _Nullable)onListenWithArguments:(id _Nullable)arguments eventSink:(nonnull FlutterEventSink)events {
        NSLog(@"%@", arguments);
        self.events = events;
        return nil;
    }
    
    - (void)dealloc {
        NSLog(@"dealloc");
    }
    
    @end
    
    @implementation AppDelegate
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        
        FlutterViewController *rootVC = (FlutterViewController *)self.window.rootViewController;
        
        // MARK: Basic Message Channel
        FlutterBasicMessageChannel *bmc = [[FlutterBasicMessageChannel alloc] initWithName:@"basic_message" binaryMessenger:rootVC.binaryMessenger codec:[FlutterStringCodec sharedInstance]];
        
        // 收到消息后回复
        [bmc setMessageHandler:^(id  _Nullable message, FlutterReply  _Nonnull callback) {
            NSString *callbackStr = [NSString stringWithFormat:@"i revice a message: %@", message];
            callback(callbackStr);
        }];
        
        // 主动发送消息 -- 需要等待flutter端初始化platform Channel
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3*NSEC_PER_SEC), dispatch_get_main_queue(), ^{
            [bmc sendMessage:@"i'm ios side, i send this message"];
        });
        
        // MARK: Method Channel
        FlutterMethodChannel *mc = [[FlutterMethodChannel alloc] initWithName:@"method_channel" binaryMessenger:rootVC.binaryMessenger codec:[FlutterStandardMethodCodec sharedInstance]];
        __weak typeof(mc) w_mc = mc;
        [mc setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
            __strong typeof(w_mc) s_mc = w_mc;
            NSLog(@"w_mc:%@", w_mc);
            NSLog(@"s_mc:%@", s_mc);
            if ([call.method isEqualToString:@"getString"]) {
                result([NSString stringWithFormat:@"%@", call.arguments]);
            }
            // ios调用flutter端 - 这里的调用不触发, 因为没有任何对象持有`mc`, 在`didFinishLaunchingWithOptions` 运行完毕后 `mc`会被释放
            [s_mc invokeMethod:@"changeColor" arguments:nil];
        }];
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5*NSEC_PER_SEC), dispatch_get_main_queue(), ^{
            // ios调用flutter端
            [mc invokeMethod:@"changeColor" arguments:nil];
        });
        
        // MARK: Event Channel
        /*
         EventChannel 是对 MethodChannel和Stream的封装,
         */
        FlutterEventChannel *ec = [FlutterEventChannel eventChannelWithName:@"event_channel" binaryMessenger:rootVC.binaryMessenger];
        [ec setStreamHandler:[[EventHander alloc] init]];
        
        
        [GeneratedPluginRegistrant registerWithRegistry:self];
        return [super application:application didFinishLaunchingWithOptions:launchOptions];
    }
    
    @end
    

    2. 原理

    ·1. Basic Message Channel & Method Channel

    Platform Channel的数据传递都是通过BinaryMessenger+Codec,在运行时BinaryMessenger的实现是_DefaultBinaryMessenger,源码位于binding.dart文件

    import 'dart:ui' as ui;
    // ......
    class _DefaultBinaryMessenger extends BinaryMessenger {
      const _DefaultBinaryMessenger._();
    
      @override
      Future<void> handlePlatformMessage(
        String channel,
        ByteData? message,
        ui.PlatformMessageResponseCallback? callback,
      ) async {
        ui.channelBuffers.push(channel, message, (ByteData? data) {
          if (callback != null)
            callback(data);
        });
      }
    
      @override
      Future<ByteData?> send(String channel, ByteData? message) {
        final Completer<ByteData?> completer = Completer<ByteData?>();
        ui.PlatformDispatcher.instance.sendPlatformMessage(channel, message, (ByteData? reply) {
          try {
            completer.complete(reply);
          } catch (exception, stack) {
            FlutterError.reportError(FlutterErrorDetails(
              exception: exception,
              stack: stack,
              library: 'services library',
              context: ErrorDescription('during a platform message response callback'),
            ));
          }
        });
        return completer.future;
      }
    
      @override
      void setMessageHandler(String channel, MessageHandler? handler) {
        if (handler == null) {
          ui.channelBuffers.clearListener(channel);
        } else {
          ui.channelBuffers.setListener(channel, (ByteData? data, ui.PlatformMessageResponseCallback callback) async {
            ByteData? response;
            try {
              response = await handler(data);
            } catch (exception, stack) {
              FlutterError.reportError(FlutterErrorDetails(
                exception: exception,
                stack: stack,
                library: 'services library',
                context: ErrorDescription('during a platform message callback'),
              ));
            } finally {
              callback(response);
            }
          });
        }
      }
    }
    

    可通过import 'package:flutter/services.dart';找到export 'src/services/binding.dart';阅读上面的代码。

    Basic Message ChannelMethod Channel的主要区别是编解码器不同,同时后者会调用_handleAsMethodCall,其目的是将二进制数据转化为MethodCall类型。

    _DefaultBinaryMessenger中,可以看到ui.PlatformDispatcher.instance.sendPlatformMessage,是Basic Message ChannelsendMethod ChannelinvokeMethod的后续调用,最终调用的是C++

    ui.channelBuffers.setListener则是setMessageHandler(Basic Message Channel)和setMethodCallHandler(Method Channel)的后续调用。这里将我们传入的handler包装在ChannelCallback内,然后推入ui.channelBuffers,当有消息从原生端传过来,ui.PlatformDispatcher会去ui.channelBuffers查询对应的Channel,从中触发回调。

    ·2. Event Channel

    Event Channel其实就是对Method Channel的封装。在使用Event Channel的使用调用了receiveBroadcastStream方法,内部代码如下

      Stream<dynamic> receiveBroadcastStream([dynamic arguments]) {
        String name = 'event_channel';
        MethodCodec codec = const StandardMethodCodec();
        MethodChannel methodChannel = MethodChannel(name, codec);
        BinaryMessenger binaryMessenger = ServicesBinding.instance.defaultBinaryMessenger;
        late StreamController<dynamic> controller;
        controller = StreamController<dynamic>.broadcast(onListen: () async {
          binaryMessenger.setMessageHandler(name, (ByteData? reply) async {
            if (reply == null) {
              controller.close();
            } else {
              try {
                controller.add(codec.decodeEnvelope(reply));
              } on PlatformException catch (e) {
                controller.addError(e);
              }
            }
            return null;
          });
          try {
            await methodChannel.invokeMethod<void>('listen', arguments);
          } catch (exception, stack) {
            FlutterError.reportError(FlutterErrorDetails(
              exception: exception,
              stack: stack,
              library: 'services library',
              context: ErrorDescription('while activating platform stream on channel $name'),
            ));
          }
        }, onCancel: () async {
          binaryMessenger.setMessageHandler(name, null);
          try {
            await methodChannel.invokeMethod<void>('cancel', arguments);
          } catch (exception, stack) {
            FlutterError.reportError(FlutterErrorDetails(
              exception: exception,
              stack: stack,
              library: 'services library',
              context: ErrorDescription('while de-activating platform stream on channel $name'),
            ));
          }
        });
        return controller.stream;
      }
    

    先将代码简化如下:

    late StreamController<dynamic> controller;
    controller = StreamController<dynamic>.broadcast(onListen: () async {
      print('on listen');
    }, onCancel: () async {
      print('on cancel');
    });
    return controller.stream;
    

    这里的重点就是broadcast方法,其onListen回调会在Stream首次被订阅的时候回调。所以当我们在外部运行_ec.receiveBroadcastStream().listen(_onEvent);Stream产生的订阅,于是onListen被调用。然后在onListen中调用:

    await methodChannel.invokeMethod<void>('listen', arguments)
    

    并在Method Channel的回调中将原生端返回的数据注入Stream

    controller.add(codec.decodeEnvelope(reply))
    

    onCancel则在Stream没有订阅者时被调用,在这里停止Method Channel

    binaryMessenger.setMessageHandler(name, null);
    try {
      await methodChannel.invokeMethod<void>('cancel', arguments);
    } catch (exception, stack) {
      ......
    }
    

    end

    精力有限,有误请指正。

    相关文章

      网友评论

          本文标题:Platform Channel (iOS)

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