背景:
我想了解关于Flutter网络请求流程,因为之前我已经熟悉了React Native的网络请求流程。所以,我希望了解一下Flutter中的网络请求是如何通过原生代码实现的。一开始,我认为Dart是一种虚拟机语言,它本身没有直接进行网络请求的能力,因此需要调用底层的原生代码。
最初
最初,我认为Flutter可能会与React Native类似,调用与NSURLSession相关的方法。我尝试在代码中设置断点,以便理解发生了什么,但却发现断点似乎无法正常工作。
看源代码
然后,我开始查阅Flutter的源代码,并找到了dio网络请求库所使用的cupertino_http库。尽管我找到了这个库,但我仍然困惑为什么NSURLSession相关方法的断点无法正常工作。我开始考虑使用符号断点来进一步调查,同时也深入研究了objc_msgSend,因为在cupertino_http库的源代码中看到了相关调用。
了解FFI
在研究Flutter的网络请求过程中,我了解到了FFI(Foreign Function Interface),即外部函数接口,以及如何将C语言函数暴露给Dart以供调用。
尝试用objc_msgSend
我打开嵌入了Flutter的iOS工程,尝试在objc_msgSend上设置断点,但发现效率太低,程序响应缓慢,无法正常处理点击等事件,即使我勾选了"automatically continue",也没有明显改善。
我尝试使用其他方法,包括越狱开发中的hook方式,使用InspectiveC,并重新学习了theos的编译和打包过程。
在这个过程中,我用iProxy将我的Mac的2222端口转发到连接iPhone的USB上的44号端口,然后使用scp将编译好的.deb包复制到手机上,但安装时遇到了报错,耗费了相当多的时间。
我还尝试使用octrace来打印出objc_msgSend的调用者和参数,这是一个用汇编实现的方法替代方案。尽管遇到了困难,但最终成功运行了代码,这让我获得了新的知识和经验。
还差一点
后来,我将生成的.a库导入到Runner工程中,并观察了代码的执行。虽然我能够打印出调用信息,但却没有看到与网络相关的内容。
豁然开朗
这让我感到困惑,直到我查看了cupertino_http的文档,才明白Dart在iOS上默认使用dart:io HttpClient的Socket来执行网络请求,而不是根据平台判断使用NSURLSession。因此,要使用NSURLSession进行网络请求,我需要直接使用cupertino_http库。
为了更清楚地了解内部情况,我使用octrace给一个Runner工程中的objc_msgSend添加钩子,以更好地监视和了解网络请求的细节。这次终于成功观察到了NSURLSession相关的网络请求。
下一步
下一步,我计划研究Dart是如何建立socket的,以确定是调用了C语言的API还是调用了系统的ABI。
收获
- 最会了octrace的使用,了解到汇编写C函数,并进行方法替换
- 知道了FFI,可以C语言函数暴露给Dart以供调用
- 复习了越狱和theos插件知识
- 在iOS上Flutter的网络请求分为socket和用NSURLSession两种,借助了FFI来调用NSURLSession相关Api。使用cupertino_http库的时候就是这个流程。