Flutter与原生系统Android之间的通信序言 Flutter是一个UI框架,但是当您需要访问超出Flutter范
序言
Flutter是一个UI框架,但是当您需要访问超出Flutter范围的功能时会发生什么,就需要从系统底层获取信息,也就是需要和特定的系统语言的交互。这就是方法通道发挥作用的地方。在这篇博文中,我们将探讨如何使用方法通道将原生Android代码无缝集成到Flutter应用中。Flutter使用灵活的系统,允许您使用可直接使用这些API的语言来调用特定于平台的API:
- Android上的Kotlin或Java
- iOS上的Swift或Objective-C
- Windows上的C++
- macOS上的Objective-C
- Linux上的C 本文只讲解和Android系统底层的交互,原理是一样的。
浅析方法通道的定义
方法通道为Flutter Dart代码和原生代码(本例中为Android Java/Kotlin)之间的通信提供了桥梁。这让您可以从Flutter应用调用原生层中的方法,让您同时享受两全其美的优势。方法通道的常见用例有如下所示:
- 调用原生API
- 在Flutter和原生代码之间共享数据
- 在Flutter和原生代码之间发送事件
- 与第三方库集成
平台通道图解
消息通过平台通道在客户端(UI)和主机(平台)之间传递,如下图所示:
消息和响应异步传递,以确保用户界面保持响应。
数据类型如何编解码
标准平台通道使用标准消息编解码器,支持对简单的JSON 类值(例如布尔值、数字、字符串、字节缓冲区以及这些值的Lists和Maps)进行高效的二进制序列化(有关详细信息,请参阅StandardMessageCodec)。当您发送和接收值时,这些值与消息之间的序列化和反序列化会自动发生。Flutter和Android之间的数据类型的映射,如下图所示:
方法通道逐步指南
- 在Flutter Dart代码中,首先创建一个方法通道并定义要在原生端调用的方法。示例代码如下:
static const platform = MethodChannel('samples.flutter.dev/battery');
// Get battery level.
String _batteryLevel = 'Unknown battery level.';
Future<void> _getBatteryLevel() async {
String batteryLevel;
try {
final result = await platform.invokeMethod<int>('getBatteryLevel');
batteryLevel = 'Battery level at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
setState(() {
_batteryLevel = batteryLevel;
});
}
方法通道的名称和调用的方法名称要具有唯一性,不能重复。
2.处理Android上的方法通道。在原生Android端,您需要设置与Flutter中定义名称相同的方法通道。这将允许您有效地处理方法调用。如果方法调用的名称相同(和getBatteryLevel进行比较),则调用Android的本地方法。结果可以返回成功,调用result.success()方法;结果可以返回异常,调用result.error()方法;结果可以没有实现,调用result.notImplemented()方法。以下是 Kotlin中的完整示例代码片段:
package com.example.batterylevel
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
class MainActivity: FlutterActivity() {
private val CHANNEL = "samples.flutter.dev/battery"
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
// This method is invoked on the main thread.
call, result ->
if (call.method == "getBatteryLevel") {
val batteryLevel = getBatteryLevel()
if (batteryLevel != -1) {
result.success(batteryLevel)
} else {
result.error("UNAVAILABLE", "Battery level not available.", null)
}
} else {
result.notImplemented()
}
}
}
private fun getBatteryLevel(): Int {
val batteryLevel: Int
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
} else {
val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
batteryLevel = intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
}
return batteryLevel
}
}
3.最后,您可以通过调用定义方法通道的相应函数从Flutter UI触发本地方法。这将通过方法通道启动通信并调用相应的本地方法。Flutter完整端的示例代码如下:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
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('samples.flutter.dev/battery');
// Get battery level.
String _batteryLevel = 'Unknown battery level.';
Future<void> _getBatteryLevel() async {
String batteryLevel;
try {
final result = await platform.invokeMethod<int>('getBatteryLevel');
batteryLevel = 'Battery level at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
setState(() {
_batteryLevel = batteryLevel;
});
}
@override
Widget build(BuildContext context) {
return Material(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: _getBatteryLevel,
child: const Text('Get Battery Level'),
),
Text(_batteryLevel),
],
),
),
);
}
}
效果图如下:
BasicMessageChannel
用于传递字符串和半结构化的信息,这个用的比较少。用于Flutter和Native之间的双向交流通信。
EventChannel
EventChannel:用于数据流(event streams)的通信。有监听功能,比如电量变化之后直接推送数据给flutter端,或者注册定位信息,经纬度变化之后,直接向Flutter端直接推送数据。EventChannel是一个stream流,会一直返回数据。
总结
方法通道(MethodChannel)是一种强大的工具,可将原生功能无缝集成到您的Flutter应用中。此方法允许您利用Flutter和原生Android代码的功能,提供统一的用户体验。基本信息通道(BasicMessageChannel)是一个可以和原生进行交换消息的方式。EventChannel是一个和原生进行流式数据交换的方式。这三种方式,各自都有自己的应用场景和优势。祝您编码愉快!
参考资料
转载自:https://juejin.cn/post/7402060970425892873