公司有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 。。。