React Native 网络层分析

文:志俊

本文原创,转载请注明作者及出处

在使用 开发中,我们熟练的采用 的方式发送请求的方式发送一个请求到服务端,但是处理这个请求的过程其实和处理 应用中发送的请求的过程是不一样的。因为处理这个请求的目标不是浏览器,而是嵌入这个应用的原生操作系统。

在处理 的请求时,分为两部分:一部分是 的运行环境,另一部分是嵌入 的 (即原生 和 i)运行环境。 内置了三种发送网络请求的方式: , 和 。但是 的运行环境和 Web 应用的运行环境不一样,所以需要在原生应用层采用自定义函数来拓展运行时(runtime)环境来处理 发出的网络请求。

请求发送方式及过程

对于常用的网络请求对象:XMLHttpRequest(XHR)、Fetch 及 WebSocket,熟悉前端开发的同学应该非常了解。 是Web开发中用得比较多的发送请求的方式, 和 也是后起之秀,在很多现代 Web 应用中得以采用。但是,在 中,这些对象的使用和 Web 应用是有差别的。当你在 JS 层调用网络请求时,其实是经历了两个过程才到达真正的服务器端。就像头部 banner 表示的那样。

XMLHttpRequest(XHR)

在 中, 由两部分组成: “前端”(front-end)和“后端”(back-end)。前端负责与 交互,后端负责在原生平台上转换 发送过来的请求为原生系统自己的请求。

这里的后端其实是一个原生平台顶层抽象的统一API层,使得 层可以调用原先系统的网络模块。例如 iOS下内置的 URLSession 模块和下的 OKHTTP模块。

Fetch

在现代 Web 浏览器中, API 提供了和 大部分相同的功能,但是 提供了一种更加简单,高效的方式来跨网络异步获取资源,同时可操纵 和 对象来复用请求。

但是在 中,为了兼容两种平台的差异,采用了依赖于 的 Fetch Polyfill 来实现这个请求对象。这就意味着我们不能像实用 Web 平台下的 对象一样来实用 下的该对象。比如采用这个对象来发送 binary 数据。当然可以采用第三方的库比如 react-native-fetch-blob 来实现相应的功能。

Websocket

作为一种新的通信协议,采用全双工通讯方式与服务器间进行通信的网络技术。

在 中, 并不是一个独立的请求,和 一样由两部分组成: “前端”(front-end)和“后端”(back-end)。前端负责与 交互,后端负责在原生平台上转换 发送过来的请求为原生系统自己的请求。在 iOS 中采用的是自己开发的 NSStream,而在 Android 系统中则是OKHTTP 模块。

查看React Native中的网络请求

在 开发中,你可以通过 的 面板中调试 部分的代码,包括断点、输出信息、断点调试等一切 调试所需的信息。但是,唯一缺少的就是网络请求的跟踪调试。我们没办法像 Web 开发那样,可以通过 中的网络面板( )来查看应用的网络请求的相关信息。

使用代理调试网络请求

虽然没有办法通过 查看应用的网络请求,但是我们可以通过 、 及 等软件设置代理,来查看追踪调试网络请求。这里使用 来作为代理。

1.首先设置 的代理端口: 打开 Filddler -> Tool -> Options -> Connects,在监听端口处填写相应的端口号,

2.在调试机器上、 或者 i模拟器模拟器中设置代理: 找到调试的机器上的网络设置中,设置当前连接的 WIFI 的代理地址

3.刷新应用,在 中查看网络请求

在代理应用中,我们可以查看请求头,返回头,返回结果等相关的网络信息。当然,还可以根据相关代理软件拦截请求,重新设置后发送。

使用 Reactotron 调试网络

上面通过设置代理的方式来查看和追踪网络请求,虽然功能强大,但是实际操作起来有些难度,上手成本比较高。通过使用 ,可以将调试的配置信息集成到应用中,方便在不同的开发环境下有相同的调试配置,节约开发配置成本。

由两部分组成,一部分是调试应用,一部分是调试配置。

1.调试应用分别有各个操作系统的 GUI 安装版本。当然,如果习惯使用命令行,也可以使用 安装

2.设置调试配置:

调试网络只是他的一个功能之一,其他还有很多强大的功能。有兴趣可以查看他的文档。

使用 Chrome Developer Tools 网络面板调试网络

默认暴露出来的接口中,是没有直接在 查看网络请求的方法的,查看 RN 源码 Libraries/Core/InitializeCore.js,注释中写着:

Sets an object’s property. If a property with the same name exists, this will replace it but maintain its descriptor configuration. By default, the property will replaced with a lazy getter. * The original property value will be preserved as original[PropertyName] so that, if necessary, it can be restored. For example, if you want to route network requests through DevTools (to trace them): * global.XMLHttpRequest = global.originalXMLHttpRequest; * @see https://github.com/facebook/react-native/issues/934

具体实现在 XHRInterceptor.js 中。原来的 被改写成了 ,所以要在 中显示 只需要替换 为 。在入口文件处设置:

当然,这样有可能会产生 , 会限制跨域请求。这时要么后端配合一下去除限制,要么使用 Allow-Control-Allow-Origin: * 插件。

React Native发送二进制数据(binary data )

由于 中 对象的底层采用的是 实现,这就限制了发送二进制数据的功能。当然 提供了一系列的方式来解决这个问题,比如: 转换二进制文件为 Base64 字符串或者采用第三方库 react-native-fetch-blob。但是并没有从底层解决这个问题。

转换二进制为 Base64 发送

到目前为止, 不能发送非序列化的数据,所以,要发送二进制数据,采用 Base64 编码的字符串是个不错的选择。

例如,你从服务器下载一张图片(注意:不是通过 从服务器获取),请求通过JavaScript 线程,再通过 提供的桥接器,最后通过原生系统的网络模块发送到服务端。服务端返回一个 Base64 编码过的图片, 线程收到返回的字符串后,会分配相应的内存,然后 会调用相应的原生模块渲染成相应图片。但是值得主要的是,这种方式会造成典型的性能问题——内存泄漏。

通过 Base64 编码的方式传输二进制文件,这里会造成一系列性能问题,这篇文章中列出了大部分性能问题及提出了相应的解决方案。

现在使用的各种方法发送二进制文件都存在各种问题,最终的解决方式是要相应的标准能够实现二进制的传输。目前, 已经支持了二进制传输。在最新版本的 层也已经支持 协议来传输二进制文件,但是,相应的原生平台的网络模块暂时还不支持。

总结

开发方式是非常不错的体验,但是,受各个平台差异和标准的限制,不得不折中处理一些问题,随之而来的是相应的性能、效率的问题。另外,采用开发,性能上和用户体验上和原生应用还是有一定差距。但是如果在原生应用中能够集成 ,会显著提高开发效率。

参考

Network layer in React Native

https://medium.com/dailyjs/network-layer-in-react-native-eec841f11861

reactotron介绍

https://infinite.red/reactotron

reactotron

https://github.com/infinitered/reactotron

Reactotron on React Native

https://github.com/infinitered/reactotron/blob/master/docs/quick-start-react-native.md

End

  • 发表于:
  • 原文链接:http://kuaibao.qq.com/s/20180118G0P75200?refer=cp_1026

扫码关注云+社区