Flutter下的通用网络框架

转载注明出处:https://blog.csdn.net/skysukai

1、背景

项目使用Flutter来做,官方已经已经有Dio提供了支持。比如下面这段代码,在理想情况下,拿到response就可以直接解析json数据了。但一般而言,服务器返回的数据都不会这么简单,所以,我们需要做的就是二次封装Dio。

Response response = await dio.post(url, data: params);

有关Dio的科普,传送门.

2、服务器数据结构

单个数据结构如下:

{
  "result": {
    "apkName": "string",
    "appInfo": "string",
    "appNam": "string",
    "appUrl": "string",
  },
  "resultInfo": {
    "resultCode": "string",
    "resultMsg": "string"
  }
}

数据结构列表如下:

{
  "result": {
    "count": 0,
    "data": [
      {
        "apkName": "string",
        "appInfo": "string",
        "appNam": "string",
        "appUrl": "string",
      }
    ],
    "pagenum": 0,
    "pagesize": 0
  },
  "resultInfo": {
    "resultCode": "string",
    "resultMsg": "string"
  }
}

如果要直接解析这些数据显然是不行的。考虑统一封装,直接给出代码如下:

class ResultInfo {

  String resultCode;

  String resultMsg;
}

class RequestResult {

  ResultInfo resultInfo;

  dynamic result;
}

将Response的成员变量data申明为ResquestResult,通过post请求得到的数据应该都是RequestResult类型的了。flutter同时提供了jsonserializable插件来为类进行json序列化和反序列化,大大提高了效率。使用jsonserializable来生成相关代码:

@JsonSerializable()
class ResultInfo {
  String resultCode;
  String resultMsg;

  ResultInfo({this.resultCode, this.resultMsg});
  factory ResultInfo.fromJson(Map<String, dynamic> json) => _$ResultInfoFromJson(json);
  Map<String, dynamic> toJson() => _$ResultInfoToJson(this);

  @override
  String toString() {
    return toJson().toString();
  }
}

@JsonSerializable()
class RequestResult {
  ResultInfo resultInfo;
  dynamic result;

  RequestResult({this.resultInfo, this.result});
  factory RequestResult.fromJson(Map<String, dynamic> json) => _$RequestResultFromJson(json);
  Map<String, dynamic> toJson() => _$RequestResultToJson(this);

  @override
  String toString() {
    return toJson().toString();
  }
}

3、网络请求的统一封装

将有关网络请求的操作封装到NetworkManager里,设置全局单例,方便调用。

3.1 基础设置

  NetworkManager._internal(String baseUrl) {

    _dio = Dio(BaseOptions(
      baseUrl: baseUrl,
      connectTimeout: 5000,
      receiveTimeout: 3000,
    ));
	//请求结果需进行json反序列化
    (_dio.transformer as DefaultTransformer).jsonDecodeCallback = _parseResponse;
	//添加Interceptor,打印日志,方便调试
    _dio.interceptors.add(NetworkLogInterceptor(requestBody: false,
        responseHeader: false,
        responseBody: true));
  }

3.2 请求结果json反序列化

由于我们自己定义了数据结构,在得到请求结果的时候需要加上反序列化函数,否则抛出异常:

  Future<RequestResult> _parseResponse(String jsonString) {
    return compute(_parseResult, jsonString);
  }

  static RequestResult _parseResult(String jsonString) {
    return RequestResult.fromJson(JsonCodec().decode(jsonString));
  }

3.3 代码主体

以post请求为例,给出代码

/**
** post方法
** onRequestSuccess:请求成功回调,回调给调用方
** onRequestFailure:请求失败回调,回调给调用方
** ParseResult:数据解析结果,回调给调用方
**/
  typedef ParseResult = dynamic Function(Map<String, dynamic> json);
  
  void post(String path, Function onRequestSuccess, Function onRequestFailure, {
    data,
    Map<String, dynamic> queryParameters,
    ParseResult parseResult,
    Options options,
    CancelToken cancelToken,
    ProgressCallback onSendProgress,
    ProgressCallback onReceiveProgress
  }) async {

    try {
      Response<RequestResult> response = await _dio.post(path,
          data: data,
          options: _checkOptions(options),
          queryParameters: queryParameters,
          cancelToken: cancelToken,
          onSendProgress: onSendProgress,
          onReceiveProgress: onReceiveProgress);

      if (response.data.resultInfo.resultCode == "200") {
        if (onRequestSuccess != null) {
          onRequestSuccess(parseResult != null && (response.data.result is Map<String, dynamic>)
              ? parseResult(response.data.result)
              : response.data.result);
        }
      } else {
        if (onRequestFailure != null) {
          onRequestFailure(int.parse(response.data.resultInfo.resultCode),
              response.data.resultInfo.resultMsg);
        }
      }
    } on DioError catch(e) {
      if (onRequestFailure != null) {
        onRequestFailure(e.response != null ? e.response.statusCode : e.type.index,
            "DioError [${e.type}]: " + (e.message ?? _dioErrorType[e.type]));
      }
    }
  }

4、网络请求的具体实现

本段以视频列表为例,说明请求的具体过程。

4.1 服务器返回数据结构

{
  "result": {
    "count": 0,
    "data": [
      {
        "iconUrl": "string",
        "likeCount": 0,
        "liked": false,
        "orientation": 0,
        "playUrl": "string",
        "publishAvatar": "string",
        "publishNick": "string",
        "publishPhotosCount": 0,
        "publishUid": 0,
        "publishVideosCount": 0,
        "published": false,
        "reviewCount": 0,
        "topicId": 0,
        "topicName": "string",
        "type": 0,
        "vid": 0
      }
    ],
    "pagenum": 0,
    "pagesize": 0
  },
  "resultInfo": {
    "resultCode": "string",
    "resultMsg": "string"
  }
}

我们需先将返回的data定义为一个类,进行json序列化及反序列化。

4.2 客户端数据构造

4.2.1 请求数据构造

发起请求时,需要先将请求的数据做一次json序列化,主要是请求数量、页码等服务器规定的参数。

@JsonSerializable()
class VideoListParam {

  int topicId;
  String udId;
  @JsonKey(name: "pagenum")
  int pageNum;
  @JsonKey(name: "pagesize")
  int pageSize;

  VideoListParam({this.topicId, this.udId, this.pageNum, this.pageSize});

  factory VideoListParam.fromJson(Map<String, dynamic> json) => _$VideoListParamFromJson(json);

  Map<String, dynamic> toJson() => _$VideoListParamToJson(this);
}

4.2.2 返回单个数据构造

data里面的item命名为VideoInfo使用jsonserializable生成相关代码:

@JsonSerializable()
class VideoInfo {

  @JsonKey(defaultValue: 0)
  int format;
  String description;
  @JsonKey(defaultValue: false, nullable: true)
  bool followed;
  @JsonKey(defaultValue: 0, nullable: true)
  int likeCount;
  @JsonKey(defaultValue: false, nullable: true)
  bool liked;
  String playUrl;
  String publishAvatar;
  String publishNick;
  @JsonKey(defaultValue: 0, nullable: true)
  int publishUid;
  @JsonKey(defaultValue: 0, nullable: true)
  int publishVideosCount;
  @JsonKey(defaultValue: 0, nullable: true)
  int publishPhotosCount;
  @JsonKey(defaultValue: 0, nullable: true)
  int reviewCount;
  @JsonKey(defaultValue: 0, nullable: true)
  int topicId;
  String topicName;
  @JsonKey(defaultValue: 0, nullable: true)
  int vid;
  @JsonKey(defaultValue: 0, nullable: true)
  int orientation;
  @JsonKey(defaultValue: true, nullable: true)
  bool published;


  VideoInfo({ this.format, this.description, this.followed, this.likeCount, this.liked, this.playUrl, this.publishAvatar, this.publishNick, this.publishUid, this.publishVideosCount, this.publishPhotosCount, this.reviewCount, this.topicId, this.topicName, this.vid, this.orientation, this.published});

  factory VideoInfo.fromJson(Map<String, dynamic> json) => _$VideoInfoFromJson(json);

  Map<String, dynamic> toJson() => _$VideoInfoToJson(this);
}

4.2.3 返回数据列表构造

@JsonSerializable()
class VideoListResult {

  int count;
  @JsonKey(name: "pagenum")
  int pageNum;
  @JsonKey(name: "pagesize")
  int pageSize;

  List<VideoInfo> data;

  VideoListResult({this.count, this.pageNum, this.pageSize});

  factory VideoListResult.fromJson(Map<String, dynamic> json) => _$VideoListResultFromJson(json);

  Map<String, dynamic> toJson() => _$VideoListResultToJson(this);
}

4.3 发起请求

/**
**onSuccess:请求成功回调
**onFailure:请求失败回调
**/
  typedef RequestSuccess = void Function(dynamic result);
  typedef RequestFailure = void Function(int code, String desc);

  void loadFromServer({RequestSuccess onSuccess, RequestFailure onFailure}) {
    int pageNum = 1;
	
    RequestSuccess requestSuccess = (dynamic result) {
      VideoListResult videoListResult = result as VideoListResult;
      if (!mounted) {
        return;
      }
      setState(() {
		……
      });
    };
	
    RequestFailure requestFailure = (int code, String desc) {
      setState(() {
		……
      });
      Log.d("result", "$code, $desc");
    };
	//请求参数
    VideoListParam videoListParam =
        VideoListParam(pageNum: pageNum, pageSize: pageSize);

      Request.getVideoList(videoListParam, requestSuccess, requestFailure);
  }
class Request {

  static void getVideoList(VideoListParam param, RequestSuccess onRequestSuccess, RequestFailure onRequestFailure) async {
    NetworkManager().post(getVideoListUrl(),
        onRequestSuccess,
        onRequestFailure,
        queryParameters: param.toJson(),
        parseResult: (Map<String, dynamic> json) => VideoListResult.fromJson(json));
  }
}

5 、总结

Flutter下的网络请求总体来说,还是很好用的。各种回调也不像java那样使用了接口,而是直接用typedef关键字来达到了接口相同的目的。希望本文对你有所帮助。

相关参考:https://medium.com/flutter-community/parsing-complex-json-in-flutter-747c46655f51
相关参考:https://www.jianshu.com/p/cb72e2f5df1c
相关参考:https://www.jianshu.com/p/1352351c7d08

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章