Flutter 和 Android 通讯 Channel 平台通道
目录
目录
Flutter 和原生间相互通讯
Flutter 和 Android 之间一般有三种通讯方式:
- MethodChanel:用于方法调用(method invocation)
- EventChannel:用于事件流的发送(event streams)
- BasicMessageChannel:用于互相主动发送消息,也可用于传递字符串或半结构化的消息
数据类型及映射关系
Dart | Java | Kotlin |
---|---|---|
null | null | null |
bool | java.lang.Boolean | Boolean |
int | java.lang.Integer | Int |
int, if 32 bits not enough | java.lang.Long | Long |
double | java.lang.Double | Double |
String | java.lang.String | String |
Uint8List | byte[] | ByteArray |
Int32List | int[] | IntArray |
Int64List | long[] | LongArray |
Float32List | float[] | FloatArray |
Float64List | double[] | DoubleArray |
List | java.util.ArrayList | List |
Map | java.util.HashMap | HashMap |
通常情况下,Dart 中的 int 都会被转换成 Java 中的 Long,而非 Int
通道和平台线程
- 目标平台向 Flutter 发起 channel 调用的时候,需要在对应平台的主线程执行。同样的,在 Flutter 向目标平台发起 channel 调用的时候,需要在 root Isolate 中执行。
- 目标平台侧的 handler 既可以在平台的主线程执行,也可以通过 Task Queue 在后台执行。
- 目标平台侧的 handler 的返回值可以在任意线程异步执行。
简单来说就是:
- 平台方法必须在 主线程 上互调,UI 相关的操作必须在 主线程 上调用
- 回调线程不做要求,即可以同步也可以异步,具体可以使用哪种方式要看 api 是怎么设计的
- 具体来说,Android 中普通
MethodCallHandler
的onMethodCall
方法回调在 主线程,但可以在任意线程执行及响应。 - 而指定
TaskQueue
为makeBackgroundTaskQueue
时,onMethodCall
方法回调在 子线程,同样可以在任意线程执行及响应。
被注解
@UiThread
标记的方法需要在 UI 线程(即main
线程、主线程)中执行。
MethodChannel
public MethodChannel(BinaryMessenger messenger, String name) {
this(messenger, name, StandardMethodCodec.INSTANCE);
}
public MethodChannel(BinaryMessenger messenger, String name, MethodCodec codec) {
this(messenger, name, codec, null);
}
public MethodChannel(BinaryMessenger messenger, String name, MethodCodec codec, BinaryMessenger.TaskQueue taskQueue) {}
Android 端代码
fun isMainThread() = Looper.getMainLooper().thread == Thread.currentThread()
fun Handler.postDelayed(delay: Long, runnable: Runnable) = postDelayed(runnable, delay)
class MethodChannelActivity : FlutterActivity() {
companion object {
private const val CHANNEL_NAME = "com.bqt.test/base_channel"
private const val METHOD_NAME = "getBatteryLevel"
}
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
val binaryMessenger: BinaryMessenger = flutterEngine.dartExecutor.binaryMessenger
MethodChannel(binaryMessenger, CHANNEL_NAME).setMethodCallHandler { call, result ->
when (call.method) {
METHOD_NAME -> onCallGetBatteryLevel(result)
else -> result.notImplemented()
}
}
}
private fun onCallGetBatteryLevel(result: MethodChannel.Result) {
println("onCallGetBatteryLevel, isMainThread:${isMainThread()}") // true
Thread {
val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
val batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
if (batteryLevel != -1) {
result.success(batteryLevel) // 可在子线程响应请求
println("result callback, isMainThread:${isMainThread()}") // false
} else {
result.error("UNAVAILABLE", "Battery level not available.", null)
}
}.start()
}
}
Flutter 端代码
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: const MyHomePage(title: '演示 MethodChannel'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
static const platform = MethodChannel('com.bqt.test/base_channel');
String _batteryLevel = '';
int _counter = 0;
void _incrementCounter() {
setState(() => _counter++); // This call to setState causes rerun the build method below
_getBatteryLevel().then((value) {
debugPrint(value);
setState(() => _batteryLevel = value);
});
}
Future<String> _getBatteryLevel() async {
try {
final int result = await platform.invokeMethod('getBatteryLevel');
return '电量 $result % .';
} on PlatformException catch (e) {
return "获取失败: '${e.message}'.";
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('这是标题')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('电量 $_batteryLevel'),
Text('点击次数 $_counter'),
],
),
),
floatingActionButton: FloatingActionButton(onPressed: _incrementCounter),
),
);
}
}
TaskQueue:回调在子线程
要在 channel 对应的平台侧的后台中执行 handler,需要使用 TaskQueue API。
class TaskQueueActivity : FlutterActivity() {
companion object {
private const val CHANNEL_NAME = "com.bqt.test/base_channel"
private const val METHOD_NAME = "getVersionCode"
}
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
val binaryMessenger: BinaryMessenger = flutterEngine.dartExecutor.binaryMessenger
val taskQueue: BinaryMessenger.TaskQueue = binaryMessenger.makeBackgroundTaskQueue()
val methodCodec: MethodCodec = StandardMethodCodec.INSTANCE
val methodChannel = MethodChannel(binaryMessenger, CHANNEL_NAME, methodCodec, taskQueue)
methodChannel.setMethodCallHandler { call, result ->
when (call.method) {
METHOD_NAME -> onCallGetVersionCode(result)
else -> result.notImplemented()
}
}
}
private fun onCallGetVersionCode(result: MethodChannel.Result) {
println("onCallGetVersionCode, isMainThread:${isMainThread()}") // false
result.success(BuildConfig.VERSION_CODE)
}
}
EventChannel
EventChannel
用于事件流的发送(event streams),可进行持续性的单向通信,只能是 Android/iOS
端主动调用,常用于传递原生设备的信息、状态等。
public EventChannel(BinaryMessenger messenger, String name) {
this(messenger, name, StandardMethodCodec.INSTANCE);
}
public EventChannel(BinaryMessenger messenger, String name, MethodCodec codec) {
this(messenger, name, codec, null);
}
public EventChannel(BinaryMessenger messenger, String name, MethodCodec codec, BinaryMessenger.TaskQueue taskQueue) {}
Android 端代码
class EventChannelActivity : FlutterActivity() {
companion object {
const val METHOD_CHANNEL_PATH = "com.bqt.test/method_channel"
const val EVENT_CHANNEL_PATH = "com.bqt.test/enent_channel"
const val mMillisInFuture: Long = 20000 // The number of millis in the future from the call to start() until the countdown is done and onFinish() is called
const val countDownInterval: Long = 1000 // The interval along the way to receive onTick(long) callbacks
}
private var mEventSink: EventChannel.EventSink? = null
private var mTimer: CountDownTimer = object : CountDownTimer(mMillisInFuture, countDownInterval) {
override fun onTick(millisUntilFinished: Long) {
println("onTick,millisUntilFinished: $millisUntilFinished, isMainThread:${isMainThread()}") // true
mEventSink?.success("剩余时间 $millisUntilFinished") // 发送消息通知
//mEventSink?.endOfStream() // 通知结束,此时 EventSink 的 onCancel 会被调用
}
override fun onFinish() {
println("onFinish,isMainThread:${isMainThread()}") // true
mEventSink?.error("error_code", "error_message", "error_details") // 发送错误通知
}
}
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
val binaryMessenger: BinaryMessenger = flutterEngine.dartExecutor.binaryMessenger
val mMethodChannel = MethodChannel(binaryMessenger, METHOD_CHANNEL_PATH)
mMethodChannel.setMethodCallHandler { call, result ->
when (call.method) {
"startTimer" -> mTimer.start().also { result.success("start success") }
"stopTimer" -> mTimer.cancel().also { result.success("stop success") }
else -> result.notImplemented()
}
}
val mEventChannel = EventChannel(binaryMessenger, EVENT_CHANNEL_PATH)
mEventChannel.setStreamHandler(object : EventChannel.StreamHandler {
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
println("onListen(建立连接),arguments: $arguments, isMainThread:${isMainThread()}") // true
mEventSink = events
}
override fun onCancel(arguments: Any?) {
println("onCancel(断开连接),arguments: $arguments, isMainThread:${isMainThread()}") // true
mEventSink = null
}
})
}
}
Flutter 端代码
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class _MyHomePageState extends State<MyHomePage> {
static const _methodChannel = MethodChannel('com.bqt.test/method_channel');
static const _eventChannel = EventChannel('com.bqt.test/enent_channel');
StreamSubscription? _streamSubscription;
String _millisUntilFinished = "默认值";
String _state = "默认值";
bool _hasStart = false;
@override
void initState() {
super.initState();
debugPrint("initState");
_streamSubscription = _eventChannel.receiveBroadcastStream().listen((data) {
debugPrint("返回值: $data");
_millisUntilFinished = data;
setState(() => _millisUntilFinished);
}, onError: (error) {
debugPrint("onError : ${error.runtimeType} ${error.toString()}");
}, onDone: () {
debugPrint("onDone");
}, cancelOnError: false); // 遇到错误时是否自动结束监听
}
void _pauseOrResume() {
StreamSubscription? temp = _streamSubscription;
if (temp != null) {
if (temp.isPaused) {
debugPrint("resume");
_streamSubscription?.resume(); // Resumes after a pause
} else {
debugPrint("pause"); // pause 后,客户端发送的数据会在 resume 时一次性全部补发回来
_streamSubscription?.pause(); // Requests that the stream pauses events until further notice
//_streamSubscription?.cancel(); // Cancels this subscription
}
}
}
void _startOrStop() {
_callStartOrStop().then((value) {
debugPrint("返回值: $value");
_state = value;
setState(() => _state);
});
_hasStart = !_hasStart;
}
Future<String> _callStartOrStop() async {
try {
if (_hasStart) {
return await _methodChannel.invokeMethod('stopTimer');
} else {
return await _methodChannel.invokeMethod('startTimer');
}
} on PlatformException catch (e) {
return "Exception: ${e.message}";
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(_state),
Text(_millisUntilFinished),
],
),
),
floatingActionButton: FloatingActionButton(onPressed: _startOrStop),
bottomNavigationBar: FloatingActionButton(onPressed: _pauseOrResume),
);
}
}
BasicMessageChannel
- BinaryCodec:二进制消息编解码器
- StringCodec:String 消息编解码器
- JSONMessageCodec:Json 消息编解码器
- StandardMessageCodec:基本类型消息编解码器
- 自定义编解码器的示例:可序列化/反序列化更多的类型
BasicMessageChannel 用于在 Flutter 和 native 互相主动发送消息,也可用于传递字符串或半结构化的消息,并支持使用自定义的消息编解码器
。
Pigeon
就是基于BasicMessageChannel
实现的。
public BasicMessageChannel(BinaryMessenger messenger, String name, MessageCodec<T> codec) {
this(messenger, name, codec, null);
}
public BasicMessageChannel(BinaryMessenger messenger, String name, MessageCodec<T> codec, BinaryMessenger.TaskQueue taskQueue) {}
Android 端代码
class BasicMessageChannelActivity : FlutterActivity() {
private var mBasicMessageChannel: BasicMessageChannel<String>? = null
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
val binaryMessenger: BinaryMessenger = flutterEngine.dartExecutor.binaryMessenger
val messageCodec: MessageCodec<String> = StringCodec.INSTANCE // 指定编解码器
mBasicMessageChannel = BasicMessageChannel(binaryMessenger, "com.bqt.test/basic_channel", messageCodec)
mBasicMessageChannel?.resizeChannelBuffer(5)
mBasicMessageChannel?.setMessageHandler { message, reply ->
println("onMessage, message: $message,isMainThread:${isMainThread()}") // true
reply.reply("已收到【$message】") // 只能 reply 一次
}
Handler(mainLooper).apply {
postDelayed(2000) { sendMessage() } // 可以多次调用 send
postDelayed(5000) { sendMessage() }
}
}
private fun sendMessage() = mBasicMessageChannel?.send("当前时间 ${System.currentTimeMillis()}") { reply ->
println("reply: $reply, isMainThread:${isMainThread()}") // true
}
}
Flutter 端代码
class _MyHomePageState extends State<MyHomePage> {
static const BasicMessageChannel<String> _channel = BasicMessageChannel('com.bqt.test/basic_channel', StringCodec());
String _message = "message";
String _response = "response";
@override
void initState() {
super.initState();
debugPrint("initState");
_channel.setMessageHandler((String? message) async {
debugPrint("message: $message"); // 收到的 native 发送的消息
_message = message ?? "null";
setState(() => _message);
return '已收到【$message】'; // 对 native 的响应
});
}
void _request() async {
final String? response = await _channel.send('来自 Dart 的请求'); // 对 native 发起请求
debugPrint("response: $response"); // 收到的 native 响应
_response = response ?? "null";
setState(() => _response);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(_response),
Text(_message),
],
),
),
floatingActionButton: FloatingActionButton(onPressed: _request),
);
}
}
2022-5-22
本文来自博客园,作者:白乾涛,转载请注明原文链接:https://www.cnblogs.com/baiqiantao/p/16296811.html