一个月带你入门Flutter:提高篇(1)——编写平台特定代码

Flutter系列的文章我会持续更新一个月左右,力求利用1个月带大家入门Flutter,抓住这波技术风口,欢迎大家关注。同时如果觉得这里代码排版不是很舒服的读者可以关注我的微信公众号“IT工匠”,我会同步更新,另外微信公众号上还有很多互联网必备资源(涉及算法、数据结构、java、深度学习、计算机网络、python、Android等互联网技术资料),欢迎大家关注、交流。

本文目录:

本文主要介绍如何编写平台特定的代码,Flutter使用了一套灵活的系统以保证我们可以调用特定平台的API,这里的“特定平台的API”可以是由Android上的Java或Kotlin代码构建的,也可以是iOS上由ObjectiveC或Swift代码构建的。

Flutter对特定平台API的调用是基于这样一套流程的:

  • Flutter代码通过平台通道(platform channel)将消息发送到原生代码层即我们的特定平台的代码层(iOS或Android)。
  • iOS或者Android层的原生代码通过对平台通道的监听,在合适的时机接收Flutter代码发送的消息。
  • iOS或者Android层的原生代码根据接收到的消息调用不同的本平台的原生API
  • iOS或者Android层的原生代码将原生API的执行结果返回给Flutter层的代码

整个过程的示意图是这样的:

注意:如果你需要在Java/Kotlin/Objective-C/Swift中使用特定平台独有的API或库,可以通过本文的内容进行实现,但是,如果你只是想根据不同的平台执行不同的代码,则不需要使用本文的办法编写平台特定的代码,只需要在Flutter应用程序通过检查defaultTargetPlatform属性的值来确定当前程序运行的平台,然后根据当前平台调用不同的Dart代码即可。

框架概述:平台通道

这里我们首先规定两个术语,我们将Flutter层的代码称为客户端代码,将Android或iOS层的平台特定代码称为宿主端代码。

使用平台通道在客户端(Flutter UI)和宿主(Android或iOS平台)之间传递消息的原理示意图如下:

发送消息和等待响应都是异步的过程,这样可以保证不会造成我们用户界面的阻塞。

在客户端(Flutter层),MethodChannel API可以发送与方法调用相对应的消息。

在宿主平台上(Android或iOS层),Android上的MethodChannelAPI和iOS上的FlutterMethodChannel API 可以通过接收从平台通道传递过来的方法调用请求从而进行对应的方法的调用,最后将调用结果通过平台通道返回给客户端。

注意: 如果需要,特定平台也可以反过来调用Flutter层的API。

平台通道支持的数据类型和解码器

标准平台通道使用标准消息编解码器,以支持简单的类似JSON值的高效二进制序列化,例如 booleans,numbers, Strings, byte buffers, List, Maps等, 当你通过平台通道进行消息的发送和接收时,被发送的消息会自动进行序列化(发送时)和反序列化(接收时)。

下表列出了Dart语言中的数据类型在Android和iOS的对应类型:

实例: 使用平台通道获取设备当前电量

下面我们来通过一个实例展示如何在Flutter中实现特定平台API的调用,我们要实现的功能是通过调用平台特定的API来获取和显示当前设备的电池电量。实现的方法是通过调用Android端的BatteryManager API和iOS端的 device.batteryLevel API 进行当前设备电量的获取。

第一步: 创建一个新的应用程序项目

首先创建一个新的应用程序:

  • 在终端运行中:flutter create batterylevel

默认情况下,模板支持使用Java编写Android代码,或使用Objective-C编写iOS代码。要使用Kotlin或Swift,请使用-i和/或-a标志:

  • 在终端中运行: flutter create -i swift -a kotlin batterylevel

第二步: 创建Flutter平台客户端

应用的State类拥有当前的应用状态,我们需要继承这个类以托管当前设备的电量。

首先,我们构造一个通道(MethodChannel),在构造通道的时候需要传入一个通道名称,这个通道名称是客户端和宿主端进行连接的纽带(或者理解为密匙),我们所指定的通道名称必须在本app内是全局唯一的,Flutter官方的建议是在通道名称前加一个唯一的“域名前缀”以避免通道名称的冲突,例如flutter_demo_code.channel_demo.chennel/battery:

构件好通道(MethodChannel)之后,我们就可以通过构建好的通道调用特定平台的方法了,这里使用的是MethodChannel.invokeMethod<T>(String method, [ dynamic arguments ]) 方法,该方法接收两个参数:

  • method:String类型的参数,表示特定平台上被调用的方法名称
  • [ dynamic arguments ]:一个数组,表示的是调用特定平台上方法时传入的参数

以我们这个例子来说,我们稍后会在Android端和iOS端分别写一个int getBatteryLevel()方法,这个方法的作用是获取当前设备的电量并返回,那么这里的method就应该传入getatteryLevel这个字符串,注意没有(),而如果还需要传递参数,直接构造一个[ dynamic arguments]类型的参数并传递进去即可,我们来看我们的代码实现(注意看注释):

最后,我们在build()中分别添加一个用于显示当前电池电量的Text和用于更新当前电池电量的RaisedButton:

第三步: 使用Java添加Android平台的特定实现

首先在Android Studio中打开您的Flutter应用的Android部分(当然,如果你习惯直接在Flutter项目中修改iOS部分也是没问题的):

  1. 启动 Android Studio
  2. 选择 ‘File > Open…’
  3. 定位到你的 Flutter app目录, 然后选择里面的 android文件夹,点击 OK
  4. 在java目录下打开 MainActivity.java

接下来,在onCreate()里做两件事:

  • 构造一个管道(MethodChannel)的实例,在构造管道的时候传入我们刚才在Flutter平台上指定的管道名称flutter_demo_code.channel_demo.chennel/battery
  • 为构造好的管道(MethodChannel)类设置MethodCallHandler,相当于对管道进行监听,当Flutter调用本管道的方法时,会在MethodCallHandler的onMethodCall()方法收到回调

具体实现代码如下:

然后我们使用Java代码,基于Android电池API来获取当前设备的电量:

最后,我们完成之前添加的onMethodCall()方法。我们需要处理管道中方法名为getBatteryLevel的调用,所以我们对call参数进行检查,看当前call参数中的方法名是否为getBatteryLevel,如果是,则调用上一步编写的getBatteryLevel()方法并将结果通过result返回给Flutter层:

现我们就可以在Android设备上运行该Flutter程序,运行效果是这样的:

需要说明的是,本小节在Android端使用的是Java语言,如果你想使用Kotlin,也是完全可以的,逻辑完全一样,只是代码语法不同而已。

第四步: 使用Objective-C添加iOS平台的特定实现

使用Xcode打开Flutter应用程序中的iOS部分(当然,如果你习惯直接在Flutter项目中修改iOS部分也是没问题的):

  1. 启动 Xcode
  2. 选择 ‘File > Open…’
  3. 定位到你的 Flutter app目录, 然后选择里面的 iOS文件夹,点击 OK
  4. 确保Xcode项目的构建没有错误。
  5. 选择 Runner > Runner ,打开AppDelegate.m

接下来,进行两步操作:

  • 在application didFinishLaunchingWithOptions:方法内部创建一个管道(FlutterMethodChannel),确保管道名称为flutter_demo_code.channel_demo.chennel/battery
  • 为创建好的管道添加一个处理方法

实现代码如下:

然后,我们基于ObjectiveC代码,通过iOS电池API来获取电池电量:

最后,我们完成之前添加的setMethodCallHandler方法,对平台方法名为getBatteryLevel的管道调用进行捕捉和处理,将处理的返回结果返回给Flutter层:

现我们就可以在Android设备上运行该Flutter程序了,由于贫穷限制了笔者,所以此处放不出真机演示图,大家自行脑补。

需要说明的是,本小节在iOS端使用的是Objective-C语言,如果你想使用Swift,也是完全可以的,逻辑完全一样,只是代码语法不同而已。

从UI代码中分离平台特定的代码

如果你希望在你的平台特定代码可以用在多个Flutter程序中,那么将这部分抽取出来做成一个packages会十分有效,关于packages部分我会在明天进行介绍,欢迎关注。

将平台特定的代码作为一个包发布

如果您希望与Flutter生态系统中的其他开发人员分享你的特定平台代码,可以将需要共享的部分抽取出来做成一个插件进行发布,关于这部分我也会在明天进行介绍,欢迎关注。

发表评论
留言与评论(共有 0 条评论)
   
验证码:

相关文章

推荐文章

'); })();