公司有APP开发需求,广义上来讲,安卓、IOS也是前端,那这个任务自然就落在我头上了。需求都还是比较简单,无非就是展示,跳转,表单等基本操作,都可以hold住。但产品经理不讲武德,来偷袭我一个web前端,上来就是一个aar,一套framework,几个代码示例和pdf,让我把这些一个相机的sdk集成到项目中,安卓和苹果端都需要,这我哪招架的住。但没办法,硬着头皮也要上。
从来没接触移动端原生开发,三月末接到需求,看了下相关文档后,并咨询原生大佬一些问题后,4月1日
开始尝试,百度,谷歌,咨询大佬,SegmentFault,StackOverflow,看github源码。终于在5月10日
完成了这个插件的所有功能并用在了项目上,抛开中间的休息时间,满打满算差不多30天
。
学习最快的方法还是要有个好老师,简单的问题,自己琢磨3天不如老师1句话。
对于精通原生开发的大佬来讲,这并不是什么难事,但对于小白来讲,还是不容易的。这过程中有不少知识需要掌握,好在都有前人做了非常多的技术总结,下面罗列一下用的知识:
Flutter篇:
Andorid篇:
iOS篇:
作为一个只有flutter经验的小白,插件开发中最简单的就是写一个基础的插件demo,可以使用最熟悉的dart语言。按着Flutter开发之插件入门一步一步来写,可以了解到插件与原生一共有3种方式,一种是触发即返回数据的MethodChannel
,另一种是监听状态流EventChannel
,还有一种BasicMessageChannel
,用于用于传递字符串和半结构化的消息,而我们主要使用前2个。
1、MethodChannel
Flutter 与 Native 端相互调用,调用后可以返回结果,可以 Native 端主动调用,也可以Flutter主动调用,属于双向通信。此方式为最常用的方式, Native 端调用需要在主线程中执行。
flutter端
在demo/lib/demo.dart
中:
import 'dart:async';
import 'package:flutter/services.dart';
class Demo {
// 声明通道名
static const MethodChannel _channel = const MethodChannel('demo');
// invokeMethod可以和原生进行通信
static Future<bool> get setup async {
return await _channel.invokeMethod('setup');
}
}
复制代码
flutter端中就是这么简单 ,这里仅是flutter触发native端,navive端主动触发暂不讨论。
Android端
在android/src/main/kotlin/com/example/demo/DemoPlugin.kt
中
import android.Manifest
import android.app.Activity
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
class DemoPlugin: FlutterPlugin, MethodCallHandler{
private lateinit var channel : MethodChannel
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
// 名称与flutter端的一致
channel = MethodChannel(binding.binaryMessenger, "demo")
channel.setMethodCallHandler(this)
}
override fun onMethodCall(call: MethodCall, result: Result) {
when (call.method) {
"setup" -> {
println(call.arguments)
result.success("setup")
}
else -> {
result.notImplemented()
}
}
}
}
复制代码
与安卓通信中,将MethodChannel
注册到主class中,主要是通道的名称要与flutter端的一致。
iOS端
在ios/Classes/SwiftDemoPlugin.swift
中
import Flutter
import UIKit
public class SwiftHzCameraPlugin: NSObject, FlutterPlugin{
public static let instance: SwiftDemoPlugin = SwiftDemoPlugin()
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "demo", binaryMessenger: registrar.messenger())
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch (call.method) {
case "setup":
print(call.arguments)
result("setup")
default:
result("nothing")
}
}
}
复制代码
和安卓端基本上是一致的,只是语法的不一样。
这里我们基本上完成一个插件最基本的功能,flutter主动与native端通信并获得返回信息。但这里只能触发一次,回调一次,并不是很灵活。有时候我们需要监听原生端的一些状态,那么EventChannel
就是必不可少的了。
2、EventChannel
EventChannel主要用于数据流(event streams)的通信, Native 端主动发送数据给 Flutter,通常用于状态的监听,比如网络变化、传感器数据等。
flutter端
在demo/lib/demo.dart
中:
import 'dart:async';
import 'package:flutter/services.dart';
class Demo {
// 依然是声明通道名,只不过这里是EventChannel
static const EventChannel _eventChannel = const EventChannel('Demo_event');
// 状态监听
static StreamController<String> statusController = StreamController.broadcast();
initEvent() {
_eventChannel
.receiveBroadcastStream()
.listen((event) async {
statusController.add(event);
}
}, onError: (dynamic error) {
print(error);
},cancelOnError: true);
}
Demo() {
initEvent();
}
}
复制代码
这里有一点变化,就是需要通过一个方法,在class
初始化的时候进行调用,用来监听原生中eventSink
传递过来的数据流,当chanel
获取到数据的时候,将数据add
到StreamController
里,方便在一般页面中进行调用。
Android端
还是在android/src/main/kotlin/com/example/demo/DemoPlugin.kt
中
import android.Manifest
import android.app.Activity
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.EventChannel
class DemoPlugin: FlutterPlugin, MethodCallHandler, ActivityAware{
private lateinit var activity: Activity
private var eventSink: EventChannel.EventSink? = null
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
// 名称与flutter端的一致
val eventChannel = EventChannel(binding.binaryMessenger, "Demo_event")
// 初始化StreamHandler
eventChannel.setStreamHandler(
object : EventChannel.StreamHandler {
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
eventSink = events
}
override fun onCancel(arguments: Any?) {
eventSink = null
}
}
)
}
}
复制代码
在kotlin中需要主动向flutter端发送消息的时候,直接使用下面方法进行通讯
eventSink?.success("通讯成功")
复制代码
iOS端
在ios/Classes/SwiftDemoPlugin.swift
中
import Flutter
import UIKit
public class SwiftHzCameraPlugin: NSObject, FlutterPlugin, HZCameraSocketDelegate{
public static let instance: SwiftDemoPlugin = SwiftDemoPlugin()
var eventSink:FlutterEventSink?
// 数据流监听
class SwiftStreamHandler: NSObject, FlutterStreamHandler {
func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
instance.eventSink = events
return nil
}
func onCancel(withArguments arguments: Any?) -> FlutterError? {
instance.eventSink = nil
return nil
}
}
public static func register(with registrar: FlutterPluginRegistrar) {
let eventChannel = FlutterEventChannel(name: "Demo_event", binaryMessenger: registrar.messenger())
eventChannel.setStreamHandler(SwiftStreamHandler())
}
}
复制代码
这里和安卓端也是大同小异,同样使用下列方法进行通讯
self.eventSink!("通讯成功")
复制代码
总结
flutter与原生通讯的也就这寥寥几行代码就可以实现,可以抽象的理解注册
–触发
,第一步都是先注册到环境中,再通过对应的事件触发。需要原生数据的时候,通过invokeMethod()
对应的方法,调用原生事件并得到需要的数据,在用result()
来返回数据。需要对原生的状态进行监听的时候,比如wifi状态,电量等,就使用onListen
将eventSink
注册到环境中,当状态改变的时候即可主动触发事件。
当然,这些功能理解后,插件开发才刚入门,学会原生端的操作才是重中之重。
← To Be Continued 。。。