您好,登录后才能下订单哦!
如何进行混合开发Flutter,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。
Flutter 作为 Google 开源的新一代跨平台、高性能 UI 框架,旨在帮助开发者高效地构建出跨平台的、UI 与交互体验一致的精美应用,推出后一直倍受开发者的青睐。
当需要开发一个全新的应用时,我们可以很方便地从零开始,完全使用 Flutter 进行开发。但如果是针对一个现有的应用,需要引入 Flutter 技术,显然使用 Flutter 全部重写一遍是不现实的。幸运的是,Flutter 很好地支持了以独立页面、甚至是 UI 片段的方式集成到现有的应用中,即所谓的混合开发模式。本文主要从一个 Android 开发的视角,谈谈 Android 平台下, Flutter 的混合开发与构建。
相信现在应该很少会有移动端开发者不知道 Flutter,这里不再做过多介绍。对于这门技术,使用过的应该绝大多数都会说好;没用过的推荐尝试一下,跑个 Demo 体验体验,有可能它就是你需要学习和掌握的最后一门新技术了。回过头来,Flutter 究竟有什么独特的魅力让它能从一众技术中脱颖而出呢?总结一下,主要有以下几点:
跨平台:可以做到一套代码完美适配 Android、iOS 平台,未来还会覆盖更多平台,大大节省了开发人力与维护成本,同时拥有出色的跨端 UI 表现一致性。
高效开发:SDK 提供了丰富的 UI 组件,开箱即用;声明式的 UI 构建方式,大大减少出错率;Debug 模式提供热重载能力,可实时预览代码变更,不需要重新编译安装。
高性能:采用自建渲染引擎,独立于系统并可单独优化;区别于 RN、WEEX,没有中间层转换的额外开销;Release 模式下代码编译为 AOT 指令,运行高效。
受益于以上的核心优势,Flutter 推出后圈了很多移动开发者的粉,各互联网大厂也纷纷将其作为一项基础技术进行研究。在 Flutter 初期,其应用场景主要是从 0 构建一个全新 App,对混合开发的支持很不友好。但作为一门跨平台的技术框架,到底还是需要依赖原生平台提供的诸多系统能力,此外还有众多现存原生 App 跃跃欲试,因此在这个需求背景下,混合开发的支持与完善至今已发展得越来越好,下面我们就用一个简单的示例开始 Android 端的 Flutter 混合开发与构建之旅。
要在一个已有的 Android Project 中使用 Flutter,需要引入一个 Flutter Module。在 Android Studio(需要确保 Flutter 插件已经成功安装并启用)中打开现有 Android 工程,通过使用 File > New > New Module… 菜单,我们可以新创建一个 Flutter 模块或是导入一个外部的 Flutter 模块。
这里以最简单的 Android App 项目为例,导入 Flutter 模块。在 Flutter 模块导入成功之后,原工程文件、结构都会发生一些变化,主要有:
settings.gradle 文件新增了以下内容。其实就是执行对应 Flutter 模块下 .android/include_flutter.groovy 脚本文件,该步骤会引入一个名为 Flutter 的 Android Library Module,同时还会引入 Flutter 模块所依赖的所有插件。
setBinding(new Binding([gradle: this])) evaluate(new File( settingsDir.parentFile, 'flutter_module/.android/include_flutter.groovy' )) include ':flutter_module' project(':flutter_module').projectDir = new File('../flutter_module')
项目结构变化,如下图所示:
在引入 Flutter 模块之前,项目中仅有 app 一个 Module;而在引入之后,可以看到除了原有的 app Module 外,Flutter Gradle 插件自动引入了额外几个子 Module:
flutter_module:指代要引入的目标 Flutter Module,不会 apply Android 相关的任何插件,主要是包含 Flutter 相关源码、资源、依赖等。
flutter:为 Flutter Gradle 插件引入的 Android Library Module;主要负责编译 flutter_module 及其依赖的第三方 Package、Plugin 的 Dart 代码,以及打包 Flutter 资源等。
device_info:为 Flutter Gradle 插件自动引入的 Flutter Android Plugin Library Module,这是因为一开始我在 flutter_module 的 pubspec.yaml 文件中添加了对 device_info 这个插件的依赖。Flutter Gradle 工具会将 flutter_module 依赖到的所有插件其 Android 平台侧的代码、资源作为一个 Library Module 引入到项目中一起参与构建。如果要查看 flutter_module 引入了哪些 Plugin,可以查看其对应目录下的 .flutter-plugins 与 .flutter-plugins-dependencies 文件,这两个文件是执行 flutter pub get 时生成的,记录了插件的本地文件目录、依赖信息等。
注意:一个工程不能包含多个 Flutter Module,最多只能引入一个,这是由 Flutter 的 Gradle 插件决定的。
完成 Flutter 模块的引入后,我们再来看看如何使用 Flutter。
首先需要在 App 模块的build.gradle脚本文件中添加对Flutter工程的依赖,只有这样 Flutter 模块才会参与到整个应用的构建中来,我们也才能够在 App 模块中调用到 Flutter 提供的 Java 层 API。如下所示:
dependencies { implementation project(':flutter') }
我们可以选择使用 Activity、Fragment 或者 View 来承载 Flutter 的 UI,这里主要介绍前面两种方式,并假设flutter_module中已经通过runApp方法渲染了一个widget。
运行 Flutter Activity。使用io.flutter.embedding.android.FlutterActivity类可以很方便的启动一个 Flutter Activity,当然我们也可以继承它并扩展自己的逻辑。示例代码如下:
FlutterActivity .withNewEngine() .build(context) .also { startActivity(it) }
运行 Flutter Fragment。可以使用FlutterFragmentActivity或者FlutterFragment来添加 Flutter UI 片段:a. 使用FlutterFragmentActivity可以自动创建并添加一个FlutterFragment;b. 手动创建FlutterFragment后添加到目标 Activity 中。示例代码如下:
val flutterFragment = FlutterFragment.withNewEngine() .dartEntrypoint(getDartEntrypointFunctionName()) .initialRoute(getInitialRoute()) .appBundlePath(getAppBundlePath()) .flutterShellArgs(FlutterShellArgs.fromIntent(intent)) .handleDeeplinking(shouldHandleDeeplinking()) .renderMode(renderMode) .transparencyMode(transparencyMode) .shouldAttachEngineToActivity(shouldAttachEngineToActivity()) .build<FlutterFragment>() fragmentManager .beginTransaction() .add( FRAGMENT_CONTAINER_ID, flutterFragment, TAG_FLUTTER_FRAGMENT ) .commit()
平台层和 Flutter 层通信。不论是开发 Plugin 还是业务逻辑,平台层与 Flutter 层通信是必不可少的,为此就需要使用到MethodChannel。平台层通过MethodChannel请求调用 Flutter 层 API 时,数据在经过打包编码后,通过 JNI、DartVM 传到 Flutter 层解码后使用;待结果计算完成后,又会重新打包编码,经过 DartVM、JNI 传回到 Native 层;同理,在 Flutter 层请求调用平台层的 API 时,数据处理是一致的,只是流转方向相反。通过这种方式,平台层与 Flutter 层就建立了一个双向的、异步的通信通道。在下面的示例代码中,Native 层使用dev.flutter.example/counter创建一个MethodChannel,并设置 Handler 接收 Dart 的远程方法调用 incrementCounter,并调用 reportCounter 将结果回传。
channel = MethodChannel(flutterEngine.dartExecutor, "dev.flutter.example/counter") channel.setMethodCallHandler { call, _ -> when (call.method) { "incrementCounter" -> { count++ channel.invokeMethod("reportCounter", count) } } }
Dart 层使用相同的名称创建 MethodChannel,并设置 Handler 处理回调结果,随后调用 incrementCounter 方法请求 counter。示例代码如下:
final _channel = MethodChannel('dev.flutter.example/counter'); _channel.setMethodCallHandler(_handleMessage); _channel.invokeMethod('incrementCounter'); Future<dynamic> _handleMessage(MethodCall call) async { if (call.method == 'reportCounter') { _count = call.arguments as int; notifyListeners(); } }
这里我们是通过手动创建 MethodChannel 进行通信的,这在进行简单通信的场景是没问题的,但在通信接口 API 比较复杂的情况就不是很适用了。
一是繁琐,因为我们需要手写大量的打包、拆包代码;二是容易出错。这个时候就轮到 Pigeon 大显身手了。Pigeon 是一个官方推出的代码生成工具,可以生成类型安全的双向通信 API 接口,具体可以参考官方的 Example,这里不再赘述。
Pigeon :https://flutter.dev/docs/development/platform-integration/platform-channels#pigeon
到这里,我们已经了解了如何在现有 Android 项目中引入并使用 Flutter,接下来我们再来探究一下 Flutter APK 的结构,看看 Flutter Tools 在这个 APK 包内到底打包了哪些东西。下面两图分别为 Debub 模式和 Release 模式下构建出来的 Flutter APK 包结构,忽略了非 Flutter 相关的项。
可以看到两个模式下的 APK 结构大致相同,说明如下:
lib/{arch}/libflutter.so:为对应架构的 Flutter Engine 共享库,负责 Flutter 渲染、JNI 通信、DartVM。如果不需要对应架构的版本,通过 abiFilters 可以 Exclude 掉。
lib/{arch}/libapp.so:只存在于 Release 模式下,共享库中包含 Dart AOT 生成的二进制指令和数据。在运行时,Flutter Engine 通过 Dynamic Load 的方式,从共享库中读取对应的可执行机器指令以及数据。
assets/flutter_assets:Flutter 引用到的相关资源
fonts:包含字体库。
FontManifest.json:引用到的字体库清单文件,json 格式,所有使用到的字体、以及字体文件在 flutter_assets 下的路径。
AssetManifest.json:其他资源清单文件,json 格式,为所有资源名称到资源路径的映射,Flutter 在加载某一项资源时,会通过这个配置清单找到对应路径的资源进行读取后加载。
kernel_blob.bin、isolate_snapshot_data、vm_snapshot_data:只存在于 Debug 模式下,分别为 DartVM 字节码与数据,其作用类似于 libapp.so,只是存在形式、打包方式不同。在 Debug 模式下,Flutter Tools 将指令和数据分别打包,主要是为了热重载(HotReload)服务的,而在 Release 模式下是统一打包成共享库。
看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注亿速云行业资讯频道,感谢您对亿速云的支持。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。