【flutter第2回】flutter与Android/iOS的通信
flutter与Android/iOS混编避免不了两者之间的通信,flutter提供了Platform-Channel。
Platform-Channel的信息传递如图:
信息的传递是异步的,为了防止UI卡顿。
通过Channel,flutter可以调用Android/iOS的代码,Android/iOS也可以调用flutter的代码。
不同语言之间的相互调用,类型首先要有映射,如下:
| Dart | Android | iOS | 
|---|---|---|
| null | null | nil (NSNull when nested) | 
| bool | java.lang.Boolean | NSNumber numberWithBool: | 
| int | java.lang.Integer | NSNumber numberWithInt: | 
| int, if 32 bits not enough | java.lang.Long | NSNumber numberWithLong: | 
| double | java.lang.Double | NSNumber numberWithDouble: | 
| String | java.lang.String | NSString | 
| Uint8List | byte[] FlutterStandardTypedData | typedDataWithBytes: | 
| Int32List | int[] FlutterStandardTypedData | typedDataWithInt32: | 
| Int64List | long[] FlutterStandardTypedData | typedDataWithInt64: | 
| Float64List | double[] FlutterStandardTypedData | typedDataWithFloat64: | 
| List | java.util.ArrayList | NSArray | 
| Map | java.util.HashMap | NSDictionary | 
示例一:Flutter调用Android/iOS的方法
1、dart里调用
首先dart定义一个MethodChannel对象传入名称'samples.flutter.io/battery',这个名称需要全局唯一。点击按钮后,调用_getBatteryLevel(),_getBatteryLevel()是异步的,函数体通过channel.invokeMethod调用Android/iOS的getBatteryLevel方法。如果Android/iOS没有定义,则会抛出异常,所以先价格try-catch。
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
...
class _MyHomePageState extends State<MyHomePage> {
  static const platform = const MethodChannel('samples.flutter.io/battery');
 // Get battery level.
  String _batteryLevel = 'Unknown battery level.';
  Future<void> _getBatteryLevel() async {
    String batteryLevel;
    try {
      final int result = await platform.invokeMethod('getBatteryLevel');
      batteryLevel = 'Battery level at $result % .';
    } on PlatformException catch (e) {
      batteryLevel = "Failed to get battery level: '${e.message}'.";
    }
    setState(() {
      _batteryLevel = batteryLevel;
    });
  }
  @override
  Widget build(BuildContext context) {
  return Material(
    child: Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          RaisedButton(
            child: Text('Get Battery Level'),
            onPressed: _getBatteryLevel,
          ),
          Text(_batteryLevel),
        ],
      ),
    ),
  );
}
}
2、Android注册方法
传入的名称samples.flutter.io/battery要保持一致,onMethodCall里去判断方法名,然后去调用对应的方法。
private int getBatteryLevel() {
        int batteryLevel = -1;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);
            batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
        } else {
            Intent intent = new ContextWrapper(getApplicationContext()).
                    registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
            batteryLevel = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) /
                    intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
        }
        return batteryLevel;
    }
    @Override
    protected void onResume() {
        super.onResume();
        FlutterView flutterView = (FlutterView) fragment.getView();
        //或者直接通过Flutter.createView获取flutterView
        new MethodChannel(flutterView, CHANNEL).setMethodCallHandler(
                new MethodChannel.MethodCallHandler() {
                    @Override
                    public void onMethodCall(MethodCall call, MethodChannel.Result result) {
                        if (call.method.equals("getBatteryLevel")) {
                            int batteryLevel = getBatteryLevel();
                            if (batteryLevel != -1) {
                                result.success(batteryLevel);
                            } else {
                                result.error("UNAVAILABLE", "Battery level not available.", null);
                            }
                        } else {
                            result.notImplemented();
                        }
                    }
                });
    }
3、iOS注册方法
往flutterViewController里注册方法flutterViewControlle哪里来?见【flutter第1回】 混编
FlutterMethodChannel* batteryChannel = [FlutterMethodChannel methodChannelWithName:@"samples.flutter.io/battery"
                                                                       binaryMessenger:flutterViewController];
    
    __weak typeof(self) weakSelf = self;
    [batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
        if ([@"getBatteryLevel" isEqualToString:call.method]) {
            int batteryLevel = [weakSelf getBatteryLevel];
            
            if (batteryLevel == -1) {
                result([FlutterError errorWithCode:@"UNAVAILABLE"
                                           message:@"Battery info unavailable"
                                           details:nil]);
            } else {
                result(@(batteryLevel));
            }
        } else {
            result(FlutterMethodNotImplemented);
        }
    }];
定义方法getBatteryLevel
- (int)getBatteryLevel {
    UIDevice* device = UIDevice.currentDevice;
    device.batteryMonitoringEnabled = YES;
    if (device.batteryState == UIDeviceBatteryStateUnknown) {
        return -1;
    } else {
        return (int)(device.batteryLevel * 100);
    }
}
示例二:Android/iOS 调用flutter的方法
channel 是允许双向通信的,Android /iOS 和 flutter的角色互换一下。以下就是关键的代码了。
1、Dart里注册方法
Channel.setMethodCallHandler((MethodCall call) async {
      assert(call.method == 'launch');
      handler(call.arguments);
    });
2、Android里调用
channel.invokeMethod("launch", type);
3、iOS里调用
  [self.channel invokeMethod:@"launch" arguments:shortcutItem.type];
转载自:https://juejin.cn/post/6844903734862938126




