热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:利用Flutter如何优雅的调用Android原生方法?

篇首语:本文由编程笔记#小编为大家整理,主要介绍了利用Flutter如何优雅的调用Android原生方法?相关的知识,希望对你有一定的参考价值。

篇首语:本文由编程笔记#小编为大家整理,主要介绍了利用 Flutter 如何优雅的调用 Android 原生方法?相关的知识,希望对你有一定的参考价值。






Flutter 的优势与缺点

Flutter 作为一个跨平台的移动 UI 框架,可以快速在 iosandroid 上构建高质量的原生用户界面。可以说是一套代码做到了多端运行,我们常说的 Flutter 跨平台,其实是 UI 跨平台,编写好了一套 UI 代码,就可以在 IOS、Android、Web 上呈现出同样的效果,节省了大量的人力成本,这也是 Flutter 越来越流行的原因之一。

å©ç¨ Flutter å¦ä½ä¼éçè°ç¨ Android åçæ¹æ³ï¼

然而 Flutter 也有它的缺点,很明显的是它并不能直接调用平台的系统功能,比如使用蓝牙、相机、GPS、音量、电池等,因此要在 Flutter 中调用这些能力就必须和原生平台进行通信。


Flutter 架构概览

了解一个框架的全貌,有助于我们从更高的视角去看待一门新技术,而避免深陷代码细节,无法自拔。首先来看一张官方给出的 Flutter 整体架构图,如下。

官方将 Flutter 框架大致分为了三层,Framework、Engine、Embedder。下面我将详细讲解一下这三层到底是什么,在实际开发中承担着怎样的角色。


Dart Framework 层

作为一个 Flutter 开发者,辛勤的码农,你每天都在这片土地挥洒着汗水。 你每天使用的各种 UI 组件(Flutter UI 控件已经快接近 400 个了),各种 Flutter Package,Flutter Plugin,层出不穷的第三方库让你感到头皮发凉。所以我不过多介绍大家都应该懂了吧,Dart 是最好的语言~


Engine 层

Flutter 引擎,官方是这样介绍它的。



Flutter 的核心,主要使用 C++ 编写,提供了 Flutter 应用所需的原语。当需要绘制新一帧的内容时,引擎将负责对需要合成的场景进行栅格化。它提供了 Flutter 核心 API 的底层实现,包括图形(通过 
Skia)、文本布局、文件及网络 IO、辅助功能支持、插件架构和 Dart 运行环境及编译环境的工具链。

我觉得官方描述得已经很详细了,总结一下就是:它负责 Flutter UI 的渲染以及宿主的交互,接入了 Engine 的就叫做宿主,如 Android 品台接入了 Flutter Engine,那么宿主就是 Android 平台。

Flutter Engine 是开源的,在 github 上可以找到,传送门 github.com/flutter/eng… 直观感受下这个 Engine 项目都是用哪些语言编写的,如下图。其实主要语言还是 C++,其中 Dart 很多是测试代码。


Embedder 平台嵌入层

官方是这样介绍它的。



平台嵌入层是用于呈现所有 Flutter 内容的原生系统应用,它充当着宿主操作系统和 Flutter 之间的粘合剂的角色。当你启动一个 Flutter 应用时,嵌入层会提供一个入口,初始化 Flutter 引擎,获取 UI 和栅格化线程,创建 Flutter 可以写入的纹理。嵌入层同时负责管理应用的生命周期,包括输入的操作(例如鼠标、键盘和触控)、窗口大小的变化、线程管理和平台消息的传递。 Flutter 拥有 Android、iOS、Windows、macOS 和 Linux 的平台嵌入层,当然,开发者可以创建自定义的嵌入层

我觉得官方这段解释得同样很好,我几乎无法有更好的解释~但我还是要谈谈自己的见解,因为从程序员的角度看代码比谈概念更具体。Embedder 层的代码同样包含于 Engine 项目中,如下图。


Embedder 层代码整体可以分为两大块


  • 各平台的 Embedder 层代码,用于接入 Flutter Engine。如 Android 平台 Embedder 是用 Java 写的,当然还有与其对应的 C++ 代码,方便 Java 调用 JNI 来和 Flutter Engine 之间通信。自然 IOS 就是 Object-C 了。
  • 共用的 Embedder 层代码,C++ 编写, 用于平台消息传递等功能。

了解了 Flutter 架构,下面开始进入实战环节。


Flutter 如何与特定平台进行通信

本小节再次强调“特定平台”这个概念,因为 Flutter 它只是一个 UI 跨平台的框架,读者需要牢记于心,凡是涉及到平台相关的功能,还是必须要由原生平台来实现。本文以 Flutter 在 Android 平台上的应用,来讲解 Flutter 该怎样和 Android 原生进行通信

Flutter 与特定平台进行通信的流程大致是这样的。


  • Flutter 通过类似 JNI 方法调用的方式与 Flutter Engine 通信
  • Flutter Engine 层调用特定平台的 Embedder 层代码
  • 特定平台接收到来自 Embedder 层的消息,进行业务处理

反之,特定平台想和 Flutter 进行通信,过程刚好和上面相反。


使用 MethodChannel 通道进行方法调用

Flutter 与特定平台进行通信需要通过“平台通道”,细心的读者可能已经发现,平台通道(Platform Channels)被官方划分在 Engine 层。平台通道分为三种类型,分别是 BasicMessageChannel、MethodChannel、EventChannel。其中 MethodChannel 它使用异步方法调用的方式进行平台通信,这也是最常用的一种方式。


编写 Flutter 端代码

这里以官方的一个手机电量查询例子来演示整个方法调用过程,Flutter 端代码如下。优雅的做法是,遵循职责单一的原则,将每一个方法通道封装成一个类。比如我这个通道是用来管理电量的,那么就叫 BatteryChannel ,所有和电量有关的方法都封装在这个类中。

class BatteryChannel {
static const _batteryChannelName = "cn.blogss/battery"; // 1.方法通道名称
static MethodChannel _batteryChannel;
static void initChannels(){
_batteryChannel = MethodChannel(_batteryChannelName); // 2\\. 实例化一个方法通道
}
// 3\\. 异步任务,通过平台通道与特定平台进行通信,获取电量,这里的宿主平台是 Android
static getBatteryLevel() async {
String batteryLevel;
try {
final int result = await _batteryChannel.invokeMethod('getBatteryLevel');
batteryLevel = 'Battery level at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
return batteryLevel;
}
}

在你需要使用这个通道的时候,在 Flutter 页面中这样做就行了。

class BatteryRoute extends StatefulWidget {
@override
State createState() {
return BatteryRouteState();
}
}
class BatteryRouteState extends State {
String _batteryLevel = 'Unknown battery level.';
// 3.异步获取到电量,然后重新渲染页面
getBatteryLevel() async{
_batteryLevel = await BatteryChannel.getBatteryLevel();
setState(() {});
}
@override
void initState() {
super.initState();
BatteryChannel.initChannels(); // 1\\. 初始化通道
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("BatteryRoute"),
centerTitle: true,
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
new ElevatedButton(
child: new Text('Get Battery Level'),
onPressed: (){
getBatteryLevel(); // 2\\. 调用通道方法
},
),
new Text(_batteryLevel),
],
),
),
);
}
}

编写 Android 端代码

Android 端代码与 Flutter 端类似,如下所示。同样这个类叫 BatteryChannel,下面是 Kotlin 写的,没了解过的读者理解上来可能有点难度。不过它也很简单,这个通道就负责电量的查询了。只需实现 MethodChannel.MethodCallHandler 接口,重写 onMethodCall 方法,这样 Flutter 端的方法调用就会进入到这里。

class BatteryChannel(flutterEngine: BinaryMessenger, context: Context): MethodChannel.MethodCallHandler {
private val batteryChannelName = "cn.blogss/battery"
private var channel: MethodChannel
private var mContext: Context
companion object {
private const val TAG = "BatteryChannel"
}
init {
Log.d(TAG, "init")
channel = MethodChannel(flutterEngine, batteryChannelName)
channel.setMethodCallHandler(this)
mContext = context;
}
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
Log.d(TAG, "onMethodCall: " + call.method)
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 (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val batteryManager = mContext.getSystemService(Context.BATTERY_SERVICE) as BatteryManager
batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
} else {
val intent = ContextWrapper(mContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
batteryLevel = intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) *
100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
}
return batteryLevel
}
}

然后我们还要在 MainActivity 中实例化一下这个通道,如下。常用的做法是在 configureFlutterEngine 这个方法中实例化我们的通道就行了,有多少个通道,就在这里实例化多少个通道。

class MainActivity: FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
BatteryChannel(flutterEngine.dartExecutor.binaryMessenger,context) // 实例化通道
}
}

最后看下效果。


写在最后

本文详细介绍了 Flutter 框架概览,分析了 Dart Framework、Engine、Embedded 三层实际开发中所处的位置,然后通过代码实战,Flutter 通过平台通道与 Android 平台进行通信,查询到了手机电量。读者应该对这两部分知识有了深刻的理解与运用,下一篇,我将继续带领大家更深层次的探索平台通道通信机制!




推荐阅读
  • IOS Run loop详解
    为什么80%的码农都做不了架构师?转自http:blog.csdn.netztp800201articledetails9240913感谢作者分享Objecti ... [详细]
  • 深入解析 Lifecycle 的实现原理
    本文将详细介绍 Android Jetpack 中 Lifecycle 组件的实现原理,帮助开发者更好地理解和使用 Lifecycle,避免常见的内存泄漏问题。 ... [详细]
  • 本文介绍了在 Java 编程中遇到的一个常见错误:对象无法转换为 long 类型,并提供了详细的解决方案。 ... [详细]
  • 在多线程并发环境中,普通变量的操作往往是线程不安全的。本文通过一个简单的例子,展示了如何使用 AtomicInteger 类及其核心的 CAS 无锁算法来保证线程安全。 ... [详细]
  • 原文网址:https:www.cnblogs.comysoceanp7476379.html目录1、AOP什么?2、需求3、解决办法1:使用静态代理4 ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 秒建一个后台管理系统?用这5个开源免费的Java项目就够了
    秒建一个后台管理系统?用这5个开源免费的Java项目就够了 ... [详细]
  • 浏览器作为我们日常不可或缺的软件工具,其背后的运作机制却鲜为人知。本文将深入探讨浏览器内核及其版本的演变历程,帮助读者更好地理解这一关键技术组件,揭示其内部运作的奥秘。 ... [详细]
  • 阿里巴巴终面技术挑战:如何利用 UDP 实现 TCP 功能?
    在阿里巴巴的技术面试中,技术总监曾提出一道关于如何利用 UDP 实现 TCP 功能的问题。当时回答得不够理想,因此事后进行了详细总结。通过与总监的进一步交流,了解到这是一道常见的阿里面试题。面试官的主要目的是考察应聘者对 UDP 和 TCP 在原理上的差异的理解,以及如何通过 UDP 实现类似 TCP 的可靠传输机制。 ... [详细]
  • 本文详细解析了Java类加载系统的父子委托机制。在Java程序中,.java源代码文件编译后会生成对应的.class字节码文件,这些字节码文件需要通过类加载器(ClassLoader)进行加载。ClassLoader采用双亲委派模型,确保类的加载过程既高效又安全,避免了类的重复加载和潜在的安全风险。该机制在Java虚拟机中扮演着至关重要的角色,确保了类加载的一致性和可靠性。 ... [详细]
  • Hyperledger Fabric 1.4 节点 SDK 快速入门指南
    本文将详细介绍如何利用 Hyperledger Fabric 1.4 的 Node.js SDK 开发应用程序。通过最新版本的 Fabric Node.js SDK,开发者可以更高效地构建和部署基于区块链的应用,实现数据的安全共享和交易处理。文章将涵盖环境配置、SDK 安装、示例代码以及常见问题的解决方法,帮助读者快速上手并掌握核心功能。 ... [详细]
  • Python错误重试让多少开发者头疼?高效解决方案出炉
    ### 优化后的摘要在处理 Python 开发中的错误重试问题时,许多开发者常常感到困扰。为了应对这一挑战,`tenacity` 库提供了一种高效的解决方案。首先,通过 `pip install tenacity` 安装该库。使用时,可以通过简单的规则配置重试策略。例如,可以设置多个重试条件,使用 `|`(或)和 `&`(与)操作符组合不同的参数,从而实现灵活的错误重试机制。此外,`tenacity` 还支持自定义等待时间、重试次数和异常处理,为开发者提供了强大的工具来提高代码的健壮性和可靠性。 ... [详细]
  • OpenAI首席执行官Sam Altman展望:人工智能的未来发展方向与挑战
    OpenAI首席执行官Sam Altman展望:人工智能的未来发展方向与挑战 ... [详细]
  • 2018年在北航听陈博士讲解时,对重入漏洞有了初步了解。最近重温了慢雾科技的相关文章,发现他们对重入漏洞的解释非常清晰明了。 ... [详细]
  • 本文详细解析了客户端与服务器之间的交互过程,重点介绍了Socket通信机制。IP地址由32位的4个8位二进制数组成,分为网络地址和主机地址两部分。通过使用 `ipconfig /all` 命令,用户可以查看详细的IP配置信息。此外,文章还介绍了如何使用 `ping` 命令测试网络连通性,例如 `ping 127.0.0.1` 可以检测本机网络是否正常。这些技术细节对于理解网络通信的基本原理具有重要意义。 ... [详细]
author-avatar
jin冫g_-_-
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有