聊聊Flutter-Flutter与Native的生死相依

Flutter与Native如何对话?

概念参考Flutter中文网

Flutter与Native间通信,是通过平台通道向Native(Android/IOS)发送消息.Navtive通过平台通道接收到消息,然后Native处理消息,然后将响应消息返回给Flutter.这样就完成了Flutter与Native的一次对话.

可以想象为:通俗的来讲就是Flutter拨打Native的电话号码,平台通道就相当于基站(BTS),通过电话号码向Native发送信号进行呼叫,呼叫成功后Native接通电话,然后Flutter可以向Native指定定义好的消息,Navtive给出响应.响应完毕后自动挂断.完成一次通话.

消息和响应是以异步.

既然是传递消息肯定有类型限制,不能什么消息都传递. 可以传递的消息类型如下:

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

当你发送和接收值时,会自动进行序列化和反序列化

如何实现通信?

本篇以Android为例实现一个语音识别的插件(使用百度语音的SDK)

Flutter要与Navtive通信,就需要构建通道(MethodChannel),构建通道完毕后,还需要知道一个电话号码(与Native定义好的一个唯一的通道名,电话号码也是唯一的)比如“asr_plugin”,这是唯一的通道名

class AsrPlugin {
//通过MethodChannel 调用原生的方法
static const MethodChannel _channel = const MethodChannel('asr_plugin');
}

上述代码,已经建立好通道了,并且尝试连接Native.要想连接Native,Native则要接受通道,接受的通道名就是Flutter定义好的通道名,这样才能与Native完整的建立通道

public static void registerWith(Registrar registrar) {
//确保通道名称一致
final MethodChannel channel = new MethodChannel(registrar.messenger(), "asr_plugin");
}

完成上述代码,Flutter已经连接上了Native,这时Flutter只要发送消息,就可以操作Native了.

代码如下,Flutter向Native发送消息,分别是识别语音、停止、取消、销毁

注意:需要将方法设置为异步的async 发送和接受响应以确保界面保持响应

static Future<String> start({Map params}) async {
return _channel.invokeMethod("start", params ?? {});
}

/// 与native通信的方法 调用native 的停止录音
static Future<String> stop() async {
return _channel.invokeMethod("stop");
}

/// 与native通信的方法 调用native 的取消录音
static Future<String> cancel() async {
return _channel.invokeMethod("cancel");
}

static Future<String> release() async {
return _channel.invokeMethod("release");
}

Native如何接受消息呢? 需要实现MethodCallHandler接口,onMethodCall方法中可以接受到Flutter发送到消息.

final MethodChannel channel = new MethodChannel(registrar.messenger(), "asr_plugin");
//设置方法处理 MethodCallHandler MethodChannel 是flutter调用原生的方法
channel.setMethodCallHandler(this);
@Override
public void onMethodCall(MethodCall call, Result result) {
//需要初始化权限
initPermission();
if (call.method.equals("getPlatformVersion")) {
result.success("Android " + android.os.Build.VERSION.RELEASE);
} else if (call.method.equals("start")) {
start(call, result);
} else if (call.method.equals("stop")) {
stop(call, result);
} else if (call.method.equals("cancel")) {
cancel(call, result);
} else if (call.method.equals("release")) {
release(call, result);
} else {
result.notImplemented();
}
}

还有一个问题,如果将Native识别到语音.返回给Flutter呢? onMethodCall(MethodCall call, Result result)在onMethodCall方法中有一个Result对象,通过Result返回给Flutter

成功的返回mResult.success(o);

失败的返回 mResult.error(s, s1, o);

Flutter处理响应:

AsrPlugin.start().then((text) {
print("------------start:" + text);
if (text != null && text.length > 0) {
setState(() {
_result = text;
});
}
}).catchError((e) {
print("------------catch:" + e.toString());
});

实现结果如下,已经识别出了语音.

实现结果

Native如何集成Flutter?

  • 创建flutter module

  • 添加flutter module依赖

  • Java/object-c 调用flutter module

  • flutter 作为原生开发的独立的页面

  • flutter 作为原生页面的一部分

Add Flutter to existing apps 此处为官方的文档,解释的非常详细,如果英语好的话可以直接看官方的文档.

这里简单介绍一下集成的步骤

通过命令行的方式创建 Flutter module

> flutter create -t module flutter_module

运行完上面的命令就会生成一个名字为flutter_module的module项目,然后将这个module放到原生项目同级的目录下.

原生项目的配置:

setting.gradle 添加如下代码

setBinding(new Binding([gradle: this])) // newevaluate(new File( // new 

settingsDir.parentFile, // new

'flutter_hybrid/.android/include_flutter.groovy' // new

))

注意flutter_hybrid需要改成你用命令创建的module的名字如:flutter_module

app.gradle中配置如下

android { 

......

compileOptions {

sourceCompatibility 1.8

targetCompatibility 1.8

}

}

dependencies {

implementation project(':flutter')

}

build 一下项目就将Flutter module 集成进来了

打开Flutter module

需要修改 main.dart 定义好跳转的Route页面

import 'dart:ui'; 

void main() => runApp(_widgetForRoute(window.defaultRouteName));

Widget _widgetForRoute(String route) {

switch (route) {

case 'list':

return _listWidget();

case 'item':

return _itemWidget();

case 'page':

return _pageWidget();

}

}

Widget _listWidget() {

return Container(

child: ListView.builder(

itemBuilder: (context, index) {

return _itemWidget();

},

itemCount: 10,

),

);

}

Widget _itemWidget() {

return Container(

child: Text(//text 必须要有 textDirection TextDirection.ltr 否则会报错

'This is Flutter route item attach new',

textDirection: TextDirection.ltr,

),

color: Color(0x00fff122),

);

}

Widget _pageWidget() {

return MyApp();

}

原生项目中如何调用flutter页面?

  • 将flutter页面作为某个view的一部分 ,通过定义好的Route名称来加载某个widget,同时需要将lifecycle生命周期传递进去
val createView = Flutter.createView(this, lifecycle, "item") 

fl_load.addView(createView)
  • 将flutter页面作为独立的页面
val beginTransaction = supportFragmentManager.beginTransaction() 

beginTransaction.replace(R.id.fl_only, Flutter.createFragment("page"))

beginTransaction.commit()

如何如何调试和热重载flutter module

如何热重载flutter module?

当打开flutter module时,并没有运行在当前的module,而是运行在原生的项目中,如果改动了module中的UI 如何热重载呢?在上面的官方文档中同样有详细的讲解

在当前的module中,点击Terminal 输入如下命令 ,就可以实现flutter module的热重载

flutter attach

本篇文章主要讲解了flutter与native通信和flutter module作为native的部分界面

文章作者: JakePrim
文章链接: https://jakeprim.cn/2019/07/04/flutter-native-1/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 JakePrim技术研究院
打赏