Flutter 中的 http 网络请求,kotlin 程序
官方建议我们输出日志过多的时候使用 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<Null> _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=$re
sponseData');}} 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.933748I/flutter (28939): method: GETI/flutter (28939): contentType: application/json; charset=utf-8I/flutter (28939): responseType: ResponseType.jsonI/flutter (28939): followRedirects: trueI/flutter (28939): connectTimeout: 10000I/flutter (28939): receiveTimeout: 10000I/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.933748I/flutter (28939): statusCode: 200I/flutter (28939): headers:I/flutter (28939): proxy-connection: Keep-aliveI/flutter (28939): etag: 063d270dc44003f39cf480b7ec6ff843I/flutter (28939): content-type: application/json;charset=utf-8I/flutter (28939): set-cookie: aliyungf_tc=AQAAAP85/D47jwIAsongemk2vUOG/ZUW; Path=/; HttpOnlyI/flutter (28939): transfer-encoding: chunkedI/flutter (28939): date: Sat, 02 Mar 2019 04:29:17 GMTI/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<String> 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<Null> _downloadImage() async {Dio dio = Dio();dio.options.baseUrl = "http://img.ipintu.cn/images/";dio.interceptors.add(LogInterceptor());String savePath = await getSavePath();try {Response response = await dio.download("/baomanwayong_04.jpg",savePath,onReceiveProgress: (int count, int total) {if(total > 0) {print((count / total * 100).toStringAsFixed(0) + "%");}});if (response.statusCode == 200) {print('下载完成');}} on DioError catch (e) {print("exception: $e");}
return;}
点击按钮调用下载方法,控制台输出信息如下:
I/flutter ( 5603): file.path = /storage/emulated/0/flutterdemo/test.jpgI/flutter ( 5603): *** Request ***I/flutter ( 5603): uri: http://img.ipintu.cn/images/baomanwayong_04.jpg
评论