【flutter第2回】flutter与Android/iOS的通信

3,700 阅读2分钟
原文链接: ie8384.com

原文: flutter.io/docs/develo…

flutter与Android/iOS混编避免不了两者之间的通信,flutter提供了Platform-Channel。

Platform-Channel的信息传递如图:
Alt text

信息的传递是异步的,为了防止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];