dio自带了个默认的转换器,查看源码会发现在DefaultTransformer 中的transformResponse函数中
if (responseBody != null &&
responseBody.isNotEmpty &&
options.responseType == ResponseType.json &&
_isJsonMime(response.headers[Headers.contentTypeHeader]?.first)) {
if (jsonDecodeCallback != null) {
return jsonDecodeCallback(responseBody);
} else {
return json.decode(responseBody);
}
}
里面会判断请求返回的数据的 ResponseType 如果是 json,会去判断有没有设置自定义json解析,如果没有会走dart默认的json解析;
所以 dio 发起网络请求如果不给设置泛型返回来的数据是经过json.decode解析的,会是一个 LinkedHashMap 类型。
但是如果接口返回的数据结构特别的复杂,dart默认的json解析用起来会特别的麻烦,而且默认是在UI线程解析json,数据量大又复杂可能会造成卡顿;
针对于复杂json解析会造成卡顿问题,dio给出的方案是使用compute在后台去解析json;
dio推荐的卡顿解决办法
// 必须是顶层函数
_parseAndDecode(String response) {
return jsonDecode(response);
}
parseJson(String text) {
return compute(_parseAndDecode, text);
}
void main() {
...
// 自定义 jsonDecodeCallback
(dio.transformer as DefaultTransformer).jsonDecodeCallback = parseJson;
runApp(MyApp());
}
但是 dio 文档里依然是用的 dart 默认的 json 解析,返回来的是一个 LinkedHashMap 类型,使用起来很不爽;倒是可以自己封装一下,把Map中的值一个一个拿出来转换成 实体类,不过如果json 复杂起来这么做要累死;
使用 json_serializable (官方推荐)
json_serializable 就不一样了,它可以把 json 解析成实体对象,其实吧它的原理也是把Map里面的内容一个一个的拿出来,最终转换成实体对象,不过它提供了代码生成器,可以一个命令生成对应的处理代码,很省事;
使用方法
一、配置
在 pubspec.yaml 中加入依赖
dependencies:
flutter:
sdk: flutter
dio: 3.0.9
json_annotation: ^3.0.1
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^1.10.0
json_serializable: ^3.3.0
json_annotation,build_runner,json_serializable是使用 json_serializable需要加的,这里是文档,最新版本号去 pub.dev上看一下就行了。
比如这样的 json 先拿简单的来讲使用方法:
{
"code": 200,
"message": "hello"
}
需要写一个对应的实体类,比如这样:
// 注解要引入这个,json_serializable官方文档没有强调这个,很多文章也都忽略了这个,没有这个是无法通过命令生成代码的
import 'package:json_annotation/json_annotation.dart';
// 记得要加入这个,这个名字通常都是 '实体类的名字.g.dart'
part 'bean.g.dart';
// 要加上这个注解
@JsonSerializable()
class Bean {
int code;
String message;
Bean();
// 下面这两个模板方法,就按照官方文档这么定义就好,用来自动生成解析代码
factory Bean.fromJson(Map<String, dynamic> json) => _$BeanFromJson(json);
Map<String, dynamic> toJson() => _$BeanToJson(this);
}
还有一些注解去官方文档看一下就好
然后打开终端切换到项目根目录执行 pub run build_runner build,如果提示找不到pub命令就改成这样 flutter packages pub run build_runner build,完成之后再项目里就能看到生成的代码了;
生成后的代码:
part of 'bean.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Bean _$BeanFromJson(Map<String, dynamic> json) {
return Bean()
..code = json['code'] as int
..message = json['message'] as String;
}
Map<String, dynamic> _$BeanToJson(Bean instance) => <String, dynamic>{
'code': instance.code,
'message': instance.message,
};
二、使用
接下来就可以在项目中直接使用了,
dio.get('url').then((response) {
var bean = Bean.fromJson(response.data);
...
});
可以在网络请求之后手动的解析json,Bean.fromJson()需要的是Map类型参数,因为dio默认的响应转换器会使用dart的默认的json解析一遍,所以 response 里面的data已经是Map类型的;
当然如果返回的数据是复杂的json这样解析也可能会造成卡顿,这里就要用到上面提到过的 dio 推荐的方法,这只自定义的 json 解析回调,用 compute 在后台解析 json ;
// Must be top-level function
_parseAndDecode(String response) {
// return jsonDecode(response);
// 只是在这个位子替换掉 dart 默认的解析方式,换成 json_serializable
return Bean.fromJson(json.decode(response));
}
parseJson(String text) {
return compute(_parseAndDecode, text);
}
这个时候网络请求返回的数据已经是自动解析过的了
dio.get('url').then((response) {
var message = response.data.message
...
});
好了就这样 dio + json_serializable + 后台解析json就完事了;
不会写文章,写的很乱,主要是记录一下。
文中使用的 Flutter SDK 版本是 1.17.5
dio 3.0.9
json_serializable 3.3.0