Platform Channel (iOS)

2022-08-03  本文已影响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

精力有限,有误请指正。

上一篇下一篇

猜你喜欢

热点阅读