likes
comments
collection

【flutter初尝ffi】 基于frb开发属于自己的shine_http组件

作者站长头像
站长
· 阅读数 63

由于dart的ffi更新日渐成熟,我决定用它做点东西。

于是做些什么就成了我思考的事情,大致思考了平时在flutter开发遇到的一些问题。

最终决定制作一套满足自己需求的组件进行尝试。

我把它叫做shine系列,可能包括但不限于:

  • http组件
  • - listview组件
  • - image组件
  • - video和直播之类的

在这里就是简单的实现了下http相关的逻辑

本次我们使用rust+dart ffi进行尝试,使用rust单纯是因为我喜欢这门语言并且pub上也有frb这个开发套件。

如果你是实际商业应用的开发者,考虑到生态的成熟度和官方的支持,ffi交叉编译的开发,还是建议优先使用c++。

为什么要开发一个http组件

一方面是我使用的dio已经很长时间没有维护了

另一方面我希望这类基础功能要有好的跨平台支持

我的话是选择了rust的reqwest进行http的开发,reqwest是一个依赖于tokio的http异步请求包,它能很容易的进行配置并将数据反序列化为rust的struct。

然后我们通过frb就可以将struct直接返回为dart的model class,给flutter使用,这个过程会比较舒服。

为了进行比较我们还会使用dart的http制作一个同样的请求,展示两者之间的过程和效率。

让我们准备一个http请求,并用apipost测试下结果:

【flutter初尝ffi】 基于frb开发属于自己的shine_http组件

首先我们制作一个dart版本的http请求:

    var c1 = DateTime.now().millisecondsSinceEpoch;
    Map<String, String> header = {
      HttpHeaders.contentTypeHeader: "application/json",
    };
    var _httpClient = http.Client();
    try {
      var uri = Uri.parse("http://httpbin.org/ip");
      var response = await _httpClient.get(uri, headers: header);
      if (response.statusCode == 200) {
        var jsonResponse =
            convert.jsonDecode(response.body) as Map<String, dynamic>;
        var bb = BaseIp.from(jsonResponse);
        var c2 = DateTime.now().millisecondsSinceEpoch;
        print(
            'bb变量返回的结果: ${bb.toString()},具体的值为:${bb.origin},耗费的时间为:${c2 - c1}');
      } else {
        print('Request failed with status: ${response.statusCode}.');
      }
    } finally {
      _httpClient.close();
    }
  }

根据返回的数据,我们可以知道,dart耗时919ms

【flutter初尝ffi】 基于frb开发属于自己的shine_http组件

然后我们准备一个rust版本的http请求

pub struct Ip {
    pub origin: String,
}

#[tokio::main(flavor = "current_thread")]
pub async fn get_item() -> anyhow::Result<Ip> {
    let retry_policy = ExponentialBackoff::builder().build_with_max_retries(1);
    let api = ClientBuilder::new(reqwest::Client::new())
        .with(RetryTransientMiddleware::new_with_policy(retry_policy))
        .build();
    let url = "http://httpbin.org/ip";
    let mut headers = HeaderMap::new();
    headers.insert("Content-Type", "application/json".parse().unwrap());
    let res = api.get(url).headers(headers).timeout(Duration::from_secs(6)).send().await?.json::<Ip>().await?;
    Ok(res)
}

交叉编译后,在dart中我们这样来调用它

    var d1 = DateTime.now().millisecondsSinceEpoch;
    try {
      var aa = await api.getItem();
      var d2 = DateTime.now().millisecondsSinceEpoch;
      print('aa变量返回的结果: ${aa.toString()},具体的值为:${aa.origin},耗费的时间为:${d2 - d1}');
    } catch (e) {
      print(e);
    }
  }

简单的重启后,我们得到基本一致的结果,在不需要手动编写dart的model class的同时,通信耗时大幅降低(个人猜测应该是反序列化的差距,毕竟rust的serde真的性能很好):

【flutter初尝ffi】 基于frb开发属于自己的shine_http组件

基本上,这个http的组件是跑通了,由于我个人不是很喜欢写dart的model class,所以应该不会将response返回出来,而是选择直接返回class使用(毕竟写struct确实比类更简单)。

虽然http组件实现的代码不多,但是也花费了我挺长的时间,在此记录一下。

第一次接触,估计大部分时间耗在交叉编译环境,frb配置,以及各种各样奇奇怪怪的坑上。

所以如果大家需要使用交叉编译来增强flutter的应用性能和跨平台性。最好提前适应一下,免得什么时候要用的时候,开始抓耳挠腮的去谷歌找解决问题的答案,就很烦。

呜呜呜!

希望大家能积极通过ffi开发让flutter的功能,性能,以及体验变得更好。
什么?不想积极,不想开发!?
无所谓,皮神会出手的!!!

【flutter初尝ffi】 基于frb开发属于自己的shine_http组件