Flutter與原生通訊的一切

Flutter與原生通訊的一切

更多關注微信公衆號:Flutter入門java

簡介

Flutter與原生之間的通訊依賴靈活的消息傳遞方式:android

  • 應用的Flutter部分經過平臺通道(platform channel)將消息發送到其應用程序的所在的宿主(iOS或Android)應用(原生應用)。
  • 宿主監聽平臺通道,並接收該消息。而後它會調用該平臺的API,並將響應發送回客戶端,即應用程序的Flutter部分。
  1. MethodChannel // Flutter與原生方法相互調用,用於方法掉用
  2. BasicMessageChannel // Flutter與原生相互發送消息,用於數據傳遞
  3. EventChannel // 原生髮送消息,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:
int, if 64 bits not enough Java.lang.BigInteger FlutterStandardBigInteger
double Java.lang.Double NSNumber numberWithDouble
String java.lang.String NSString
Unit8List 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

MethodChannel(互相調用方法)

Android調用Flutter方法:git

Android:github

  1. 初始化MethodChannel
//初始化,傳遞1. flutterView(MainActivity中getFlutter獲取),2. name常量,Flutter中使用同名常量
MethodChannel methodChannel = new MethodChannel(flutterView, 「testflutter」);
複製代碼
  1. 調用Flutter方法
private void invokeFlutterMethod() {
  if (this.mMethodChannel != null) {
    this.mMethodChannel.invokeMethod("flutterMethod", "native參數", new MethodChannel.Result() {
      @Override
      public void success(Object o) {
        Toast.makeText(mContext, o.toString(), Toast.LENGTH_LONG).show();
      }
      @Override
      public void error(String s, String s1, Object o) {
      }
      @Override
      public void notImplemented() {
      }
    });
  }
}
複製代碼

經過MethodChannel調用invokeMethod("方法名","傳遞參數",[Flutter返回參數回調,非必須]);web

Flutter:微信

  1. 初始化MethodChannel
static const methodChannel = const MethodChannel('testflutter');
複製代碼
  1. 添加處理方法到MethodChannel
methodChannel.setMethodCallHandler(_addNativeMethod);
複製代碼
  1. 處理android調用的方法,根據方法名
Future<dynamic> _addNativeMethod(MethodCall methodCall) async {
  switch (methodCall.method) {
    case 'flutterMethod':
      setState(() {
        _calledFromNative = 'flutter method called from native with param ' + methodCall.arguments;
      });
      return 'flutter method called from native with param ' + methodCall.arguments;
      break;
  }
}
//其中,return返回的數據在Android的回調中接收
複製代碼

Flutter調用Android方法:markdown

Android:數據結構

  1. 初始化MethodChannel,並添加自定義plugin
MethodChannel methodChannel = new MethodChannel(flutterView, METHOD_CHANNEL);
methodChannel.setMethodCallHandler(plugin);
複製代碼
  1. 自定義的plugin實現MethodChannel.MethodCallHandler接口的onMethodCall方法
@Override
public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
  // Flutter調用Native的方法
  if (methodCall.method.equals("getBatteryLevel")) {
    int batteryLevel = getBatteryLevel();
    if (batteryLevel != -1) {
      result.success(batteryLevel);
    } else {
      result.error("UNAVALIABLE", "battery level unavaliable", null);
    }
  } else {
    result.notImplemented();
  }
}
//在onMethodCall中監聽Flutter調用什麼名字的方法(此處getBatterLevel),經過result返回方法的執行結果。
複製代碼

Flutter:異步

  1. 初始化MethodChannel
static const methodChannel = const MethodChannel('testflutter');
複製代碼
  1. 調用Android的方法,接收返回數據
//方法通道的方法是異步的
Future<Null> _getBatteryLevel() async {
  String batteryLevel;
  try {
    final int result = await methodChannel.invokeMethod('getBatteryLevel');
    batteryLevel = 'Battery level $result .';
  } on PlatformException catch (e) {
    batteryLevel = 'Battery level unknown ${e.message}';
  }  
  setState(() {
    _batteryLevel = batteryLevel;
  });
}
複製代碼

BasicMessageChannel(互相發送消息)

Android給Flutter發消息:async

Android:

  1. 初始化BasicMethodChannel
BasicMessageChannel messageChannel = new BasicMessageChannel<>(flutterView, "messageChannel", StandardMessageCodec.INSTANCE);
複製代碼
  1. 調用發送消息的方法
private void sendMessageToFlutter() {
  if (this.mBasicMessageChannel != null) {
    this.mBasicMessageChannel.send("Message From Native");
  }
}
複製代碼

Flutter:

  1. 初始化BasicMessageChannel
static const basicMessageChannel = BasicMessageChannel('messageChannel', StandardMessageCodec());
複製代碼
  1. 添加接收信息處理方法
void _listenMessageFromNative() {
  basicMessageChannel.setMessageHandler(_receiveMessageFromNative);
}
複製代碼
  1. 處理接收的數據
//Flutter接收Native發來的消息
Future<dynamic> _receiveMessageFromNative(Object result) async {
  setState(() {
    _messageFromNative = result.toString();
  });
}
複製代碼

Flutter給Android發消息:

Android:

  1. 初始化BasicMessageChannel並添加plugin給handler
BasicMessageChannel messageChannel = new BasicMessageChannel<>(flutterView, "messageChannel", StandardMessageCodec.INSTANCE);
messageChannel.setMessageHandler(plugin);
複製代碼
  1. plugin實現BasicMessageChannel.MessageHandler接口的onMessage方法,處理接收到的信息
@Override
public void onMessage(Object o, BasicMessageChannel.Reply reply) {
  Toast.makeText(mContext, o.toString(), Toast.LENGTH_LONG).show();
  reply.reply(o.toString()+" back from native");
}
//reply返回數據給Flutter
複製代碼

Flutter:

  1. 初始化BasicMessageChannel
static const basicMessageChannel = BasicMessageChannel('messageChannel', StandardMessageCodec());
複製代碼
  1. 發送消息給Android並接收返回數據
Future<dynamic> _sendMessageToNative(String message) async {
  String reply = await basicMessageChannel.send(message);
  print(reply);
  setState(() {
    _replayFromNative = reply;
  });
  return reply;
}
複製代碼

EventChannel(原生髮送消息,Flutter接收)

Android:

  1. 初始化EventChannel並添加plugin給handler
EventChannel eventChannel = new EventChannel(flutterView, EVENT_CHANNEL);
eventChannel.setStreamHandler(plugin);
複製代碼
  1. plugin實現EventChannel.StreamHandler接口及onListen、onCancel方法
  2. 在onListen中經過EventChannel.EventShink的實例發消息給Flutter
@Override
public void onListen(Object o, EventChannel.EventSink eventSink) {
  BroadcastReceiver chargingBroadcastReceiver = createChargingBroadcaseReceiver(eventSink);
  mContext.registerReceiver(chargingBroadcastReceiver,new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
}
@Override
public void onCancel(Object o) {
}
private BroadcastReceiver createChargingBroadcaseReceiver(EventChannel.EventSink eventSink) {
  return new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
      int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
      if (status == BatteryManager.BATTERY_STATUS_UNKNOWN) {
        eventSink.error("UNAVALIABLE", "charging status is unavailable", null);
      } else {
        boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING;
        eventSink.success(isCharging ? "charging" : "disCharging");
      }
    }
  };
}
複製代碼

Flutter:

  1. 初始化EventChannel
static const _eventChannel = const EventChannel('charging');
複製代碼
  1. 添加接收數據方法
void listenNativeEvent() {
  _eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError);
}
//接收返回的數據
void _onEvent(Object object) {
  String s = "Battery is ${object == 'charging' ? '' : 'dis'}Charging";
  setState(() {
    _batteryStatus = s;
  });
}
複製代碼

橋接View給Flutter使用

Android:

  1. 自定義View,繼承自PlatformView
public class MyTextview implements PlatformView {
  TextView t;
  public MyTextview(Context context, MessageCodec<Object> messenger, int id, Map<String, Object> params){
    TextView textView = new TextView(context);
    //獲取參數,是否有傳遞參數過來
    if (params.containsKey("text")){
      textView.setText(params.get("text").toString());
    }
    this.t = textView;
  }
  @Override
  public View getView() {
    return t;
  }
  
  @Override
  public void dispose() {
  }
}
複製代碼
  1. 實現PlatformViewFactory
public class TextViewFactory extends PlatformViewFactory {
  private MessageCodec<Object> messageCodec;
  public TextViewFactory(MessageCodec<Object> createArgsCodec) {
    super(createArgsCodec);
    this.messageCodec = createArgsCodec;
  }
  
  @Override
  public PlatformView create(Context context, int i, Object o) {
    return new MyTextview(context, messageCodec, i, (Map<String, Object>) o);
  }
}
複製代碼
  1. 註冊View給Flutter使用
registrar.platformViewRegistry().registerViewFactory("TextView", new TextViewFactory(new StandardMessageCodec()));
//起名叫TextView,給Flutter用作viewType
複製代碼

Flutter:

  1. 使用橋接的View
AndroidView(
  viewType: 'TextView',
  creationParams: {'text': 'TTTeeeXXXttt'},
  creationParamsCodec: new StandardMessageCodec(),
),//其中creationParams,creationParamsCodec必須同時存在或不存在
複製代碼

以上,正文結束;

官方還有一些其餘建議

發佈pub使用:

  1. Hosted packages(發佈到pub.dartlang.org)

    $flutter packages pub publish --dry-run
    $flutter packages pub publish
    複製代碼

    在yaml文件和其餘dependencies同樣使用。

  2. Git packages(遠端)

    代碼上傳到Git,並打一個tag

    yaml文件引用

    dependencies:
    	flutter_remote_package:
    		git:
    			url: git@gitlab....
    			ref: 0.0.1 //能夠是commit、branch、tag
    複製代碼
  3. 本地

    在Flutter App根目錄下建立plugins文件夾,把插件移動到plugins下。

    dependencies:
    	flutter_plugin_batterylevel:
    		path: plugins/flutter_plugin_batterylevel
    複製代碼

    以上限於在建立工程的時候,使用的是plugins建立的,有時候會在本身的Android或iOS工程內部開發,就不這麼方便分離發佈了。

有時候須要到UI thread執行channelMethod,在Android上須要post一個Runnable到Android UI線程。

new Handler(Looper.getMainLooper()).post(new Runnable() {
  @Override
  public void run(){
    // call the desired channel message here.
  }
})
複製代碼
  1. 所謂的「傳View」的本質是傳遞紋理ID,咱們只須要明白Flutter是經過Presentation實現了外接紋理,在建立Presentation時,傳入FlutterView對應的Context和建立出來的一個虛擬顯示屏對象,使得Flutter能夠直接經過ID找到並使用Native建立出來的紋理數據。

  2. 事件處理,從Native傳遞到Flutter這一階段Flutter按照本身的規則處理事件,若是AndroidView獲取到了事件,事件會被封裝成相應的Native端的事件經過方法通道傳回Native,Native再處理事件。

    對於可能出現的滑動時間衝突,能夠參考官方註釋:

    /// For example, with the following setup vertical drags will not be dispatched to the Android view as the vertical drag gesture is claimed by the parent [GestureDetector].
    /// 
    /// GestureDetector(
    /// onVerticalDragStart: (DragStartDetails d) {},
    /// child: AndroidView(
    /// viewType: 'webview',
    /// gestureRecognizers: <OneSequenceGestureRecognizer>[],
    /// ),
    /// )
    /// 
    /// To get the [AndroidView] to claim the vertical drag gestures we can pass a vertical drag gesture recognizer in [gestureRecognizers] e.g:
    /// 
    /// GestureDetector(
    /// onVerticalDragStart: (DragStartDetails d) {},
    /// child: SizedBox(
    /// width: 200.0,
    /// height: 100.0,
    /// child: AndroidView(
    /// viewType: 'webview',
    /// gestureRecognizers: <OneSequenceGestureRecognizer>[new VerticalDragGestureRecognizer()],
    /// ),
    /// ),
    /// )
    複製代碼

    [github]github.com/damengzai/f…

參考:

在Flutter中嵌入Native組件的正確姿式是... ——閒魚技術

相關文章
相關標籤/搜索