Flutter中的http网络请求

var response = await request.close();
if (response.statusCode == 200) {
print(‘请求成功’);
var responseBody = await response.transform(utf8.decoder).join();
print(‘responseBody = $responseBody’);
} else {
print(‘请求失败’);
}
}

以上代码中httpClient.getUrl(uri)方法即是根据uri发起get请求,post请求用法和get请求只是调用方法不同,包括参数设置等其他用法都一样。请求抓包结果如下:

  • get请求
  • post请求

    请求成功之后以上代码输出结果如下:

I/flutter (19183): 请求成功
I/flutter (19183): responseBody={“resultcode”:“200”,“reason”:“查询成功”,“result”:{“sk”:{“temp”:“8”,“wind_direction”:“西南风”,“wind_strength”:“2级”,“humidity”:“51%”,“time”:“21:54”},“today”:{“temperature”:“1℃15℃",“weather”:“晴”,“weather_id”:{“fa”:“00”,“fb”:“00”},“wind”:“南风微风”,“week”:“星期五”,“city”:“北京”,“date_y”:“2019年03月01日”,“dressing_index”:“较冷”,“dressing_advice”:“建议着厚外套加毛衣等服装。年老体弱者宜着大衣、呢外套加羊毛衫。”,“uv_index”:“中等”,“comfort_index”:“”,“wash_index”:“较适宜”,“travel_index”:“较不宜”,“exercise_index”:“较不宜”,“drying_index”:“”},“future”:[{“temperature”:"1℃15℃”,“weather”:“晴”,“weather_id”:{“fa”:“00”,“fb”:“00”},“wind”:“南风微风”,“week”:“星期五”,“date”:“20190301”},{“temperature”:“3℃15℃",“weather”:“霾”,“weather_id”:{“fa”:“53”,“fb”:“53”},“wind”:“西南风微风”,“week”:“星期六”,“date”:“20190302”},{“temperature”:"3℃15℃”,“weather”:“霾转晴”,“weather_id”:{“fa”:“53”,“fb”:“00”},“wind”:“西南风微风”,"

由此可以看出结果输出不完整,部分字符串被丢弃了,前段时间有个同学留言问过这个问题,在这里说明一下。Flutter官方文档有这个问题的说明:

The Dart print() function outputs to the system console, which you can view using flutter logs (which is basically a wrapper around adb logcat).

If you output too much at once, then Android sometimes discards some log lines. To avoid this, you can use debugPrint(), from Flutter’s foundation library. This is a wrapper around print which throttles the output to a level that avoids being dropped by Android’s kernel.

具体地址在:

https://flutter.dev/docs/testing/debugging#print-and-debugprint-with-flutter-logs

官方建议我们输出日志过多的时候使用debugPrint()函数,作者使用debugPrint()函数之后发现依然是输出不完整,查看debugPrint()函数的具体代码实现后发现以上输出的内容是一行的内容,一行内容过多的情况下debugPrint()函数依然无法完整进行输出,最后我采用了一个技巧,对以上返回的responseBody结果进行了简单处理,根据特殊字符串进行字符串替换,加入换行符,代码如下:

var responseBody = await response.transform(utf8.decoder).join();
//‘{’符号前加个换行符
responseBody = responseBody.replaceAll(“{”, “\n{”);
debugPrint(‘responseBody=$responseBody’);

于是输出了完整的请求返回结果:

I/flutter (19183): 请求成功
I/flutter (19183): responseBody=
I/flutter (19183): {“resultcode”:“200”,“reason”:“successed!”,“result”:
I/flutter (19183): {“sk”:
I/flutter (19183): {“temp”:“8”,“wind_direction”:“西南风”,“wind_strength”:“2级”,“humidity”:“51%”,“time”:“22:09”},“today”:
I/flutter (19183): {“temperature”:“1℃~15℃”,“weather”:“晴”,“weather_id”:
I/flutter (19183): {“fa”:“00”,“fb”:“00”},“wind”:“南风微风”,“week”:“星期五”,“city”:“北京”,“date_y”:“2019年03月01日”,“dressing_index”:“较冷”,“dressing_advice”:“建议着厚外套加毛衣等服装。年老体弱者宜着大衣、呢外套加羊毛衫。”,“uv_index”:“中等”,“comfort_index”:“”,“wash_index”:“较适宜”,“travel_index”:“较不宜”,“exercise_index”:“较不宜”,“drying_index”:“”},“future”:[
I/flutter (19183): {“temperature”:“1℃~15℃”,“weather”:“晴”,“weather_id”:
I/flutter (19183): {“fa”:“00”,“fb”:“00”},“wind”:“南风微风”,“week”:“星期五”,“date”:“20190301”},
I/flutter (19183): {“temperature”:“3℃~15℃”,“weather”:“霾”,“weather_id”:
I/flutter (19183): {“fa”:“53”,“fb”:“53”},“wind”:“西南风微风”,“week”:“星期六”,“date”:“20190302”},
I/flutter (19183): {“temperature”:“3℃~15℃”,“weather”:“霾转晴”,“weather_id”:
I/flutter (19183): {“fa”:“53”,“fb”:“00”},“wind”:“西南风微风”,“week”:“星期日”,“date”:“20190303”},
I/flutter (19183): {“temperature”:“2℃~15℃”,“weather”:“晴”,“weather_id”:
I/flutter (19183): {“fa”:“00”,“fb”:“00”},“wind”:“北风微风”,“week”:“星期一”,“date”:“20190304”},
I/flutter (19183): {“temperature”:“3℃~16℃”,“weather”:“晴”,“weather_id”:
I/flutter (19183): {“fa”:“00”,“fb”:“00”},“wind”:“北风微风”,“week”:“星期二”,“date”:“20190305”},
I/flutter (19183): {“temperature”:“3℃~15℃”,“weather”:“霾”,“weather_id”:
I/flutter (19183): {“fa”:“53”,“fb”:“53”},“wind”:“西南风微风”,“week”:“星期三”,“date”:“20190306”},
I/flutter (19183): {“temperature”:“3℃~15℃”,“weather”:“霾”,“weather_id”:
I/flutter (19183): {“fa”:“53”,“fb”:“53”},“wind”:“西南风微风”,“week”:“星期四”,“date”:“20190307”}]},“error_code”:0}

第三方网络库dio

dio是目前使用最多的Flutter三方网络库,它是对http请求的一个封装,功能非常强大,支持拦截器、全局配置、表单数据、请求取消、文件下载、超时等。目前dio的最新版本为2.0.15,2.x版本对1.x版本代码进行了一些重构,所以之前使用过1.x版本的同学,再升级到2.x时要注意需要修改一些代码。

下面基于2.0.15版本举个请求天气信息的例子:

Future _getWeatherInfo() async {
Dio dio = Dio();
//设置代理
DefaultHttpClientAdapter adapter = dio.httpClientAdapter;
adapter.onHttpClientCreate = (HttpClient client) {
if(client == null) {
client = HttpClient();
}
client.findProxy = (url) {
return HttpClient.findProxyFromEnvironment(url, environment: {“http_proxy”: ‘http://192.168.124.94:8888’,});
};
return client;
};
dio.options.baseUrl = “http://v.juhe.cn/”;
//设置连接超时时间
dio.options.connectTimeout = 10000;
//设置数据接收超时时间
dio.options.receiveTimeout = 10000;
try {
//以表单的形式设置请求参数
Map<String, String> queryParameters = {‘format’: ‘2’, ‘key’: ‘939e592487c33b12c509f757500888b5’, ‘lon’: ‘116.39277’, ‘lat’: ‘39.933748’};
Response response = await dio.get(“/weather/geo”, queryParameters: queryParameters);
if (response.statusCode == 200) {
print(‘请求成功’);
var responseData = response.data.toString();
responseData = responseData.replaceAll(“{”, “\n{”);
debugPrint(‘response.data=$responseData’);
}
} on DioError catch (e) {
print(“exception: $e”);
}

return;
}

关于代理设置代码块,在Dio 2.x版本和1.x版本上设置的方式是不同的,这是因为Dio 2.x版本代码重构的原因,1.x代码上onHttpClientCreate方法放在了Dio类中,而2.x版本将该方法封装到了一个名为DefaultHttpClientAdapter的类中,Dio类中的httpClientAdapter即是默认实现了DefaultHttpClientAdapter类的对象,所以此时根据httpClientAdapter对象来设置onHttpClientCreate为自定义方法即可。

1.x版本代理设置方法的代码块如下:

Dio dio = Dio();
dio.onHttpClientCreate = (HttpClient client) {
if(client == null) {
client = HttpClient();
}
client.findProxy = (url) {
return HttpClient.findProxyFromEnvironment(url, environment: {“http_proxy”: ‘http://192.168.124.94:8888’,});
};
return client;
};

拦截器

使用Dio处理网络请求可以设置拦截器,dio中定义了两个拦截器:LogInterceptor和CookieManager,这两个拦截器都是抽象类Interceptor的实现,具体是对抽象类中onRequest、onResponse、onError三个方法的实现,我们也可以自定义拦截器处理一些自己的需求。下面使用LogInterceptor来举例说明拦截器的作用:

//在上文dio使用的代码中加入如下代码,即添加了Log拦截器
dio.interceptors.add(LogInterceptor());

添加拦截器之后,执行请求,会看到控制台打印如下信息:

I/flutter (28939): *** Request ***
I/flutter (28939): uri: http://v.juhe.cn/weather/geo?format=2&key=939e592487c33b12c509f757500888b5&lon=116.39277&lat=39.933748
I/flutter (28939): method: GET
I/flutter (28939): contentType: application/json; charset=utf-8
I/flutter (28939): responseType: ResponseType.json
I/flutter (28939): followRedirects: true
I/flutter (28939): connectTimeout: 10000
I/flutter (28939): receiveTimeout: 10000
I/flutter (28939): extra: {}
I/flutter (28939): header:
I/flutter (28939):
I/flutter (28939): *** Response ***
I/flutter (28939): uri: http://v.juhe.cn/weather/geo?format=2&key=939e592487c33b12c509f757500888b5&lon=116.39277&lat=39.933748
I/flutter (28939): statusCode: 200
I/flutter (28939): headers:
I/flutter (28939): proxy-connection: Keep-alive
I/flutter (28939): etag: 063d270dc44003f39cf480b7ec6ff843
I/flutter (28939): content-type: application/json;charset=utf-8
I/flutter (28939): set-cookie: aliyungf_tc=AQAAAP85/D47jwIAsongemk2vUOG/ZUW; Path=/; HttpOnly
I/flutter (28939): transfer-encoding: chunked
I/flutter (28939): date: Sat, 02 Mar 2019 04:29:17 GMT
I/flutter (28939):
I/flutter (28939):

由打印结果可以看出LogInterceptor拦截器其实就是对request和response相关信息的打印,这有助于我们对http请求进行调试。

全局配置

由以上代码可以看出使用dio实现网络请求时,可以通过dio.options的进行全局通用配置,比如baseUrl、超时时间、请求的header信息等。

表单数据

使用post请求我们可以使用form表单的形式发送参数数据,代码如下:

Map<String, String> queryParameters = {‘format’: ‘2’, ‘key’: ‘939e592487c33b12c509f757500888b5’, ‘lon’: ‘116.39277’, ‘lat’: ‘39.933748’};
Response response = await dio.post(“/weather/geo”, data: FormData.from(queryParameters), options: Options());

通过抓取请求包,我们会发现content-type类型如下:

请求取消

比如跳转到某个页面请求了一条远程数据,但在数据没有回来之前关闭了该页面,那这条请求是可以在关闭页面时取消掉的,这要求你在发请求时使用cancelToken参数,如下:

CancelToken _cancelToken = CancelToken();
Map<String, String> queryParameters = {‘format’: ‘2’, ‘key’: ‘939e592487c33b12c509f757500888b5’, ‘lon’: ‘116.39277’, ‘lat’: ‘39.933748’};
Response response = await dio.post(“/weather/geo”, data: FormData.from(queryParameters), cancelToken: _cancelToken);

在页面退出之前可以通过调用_cancelToken.cancel()方法取消该请求。多个请求可以使用同一个CancelToken对象,故调用该对象的cancel()方法时所有的未完成的请求均会被取消。

文件下载

Dio中实现的文件下载功能也非常简单好用,实例代码如下:

Future getSavePath() async {
Directory externalStorageDir = await getExternalStorageDirectory();
String externalStoragePath = externalStorageDir.path + “/flutterdemo”;
Directory dir = Directory(externalStoragePath);
if(!dir.existsSync()) {
dir.createSync();
}
File file = File(dir.path + “/test.jpg”);
if(!file.existsSync()) {
file.createSync();
}
print(‘file.path = ${file.path}’);
return file.path;
}

Future _downloadImage() async {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

在此为大家准备了四节优质的Android高级进阶视频:

架构师项目实战——全球首批Android开发者对Android架构的见解

附相关架构及资料

image.png

往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术,群内还有技术大牛一起讨论交流解决问题。

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!

往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术,群内还有技术大牛一起讨论交流解决问题。

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值