美文网首页
Flutter网络框架

Flutter网络框架

作者: 肖散 | 来源:发表于2020-02-18 15:08 被阅读0次

[TOC]

网络框架主要包含2个方面

  1. 网络访问
  2. 数据解析

1 网络访问

dio

  # 网络请求
  dio: ^2.0.7

get用queryParameters参数,post用data参数。extra参数并没有什么实际的作用

这里技术胖的博客有点的问题, post是用的queryParameters的参数,这个参数其实应该是get使用的。post使用的应该是data参数。

  1. 在最后拼接URI的时候用到的只是queryParameters参数。DefaultHttpClientAdapter.fetch中最后openurl的时候用的就是 URI。只拼接了 queryParameters,其他的什么extra并没加进来

  2. 在head拼接的时候 dio._transformData实际上只拼接了data的相关数据,其他的数据是并没有放到head中的。
    dio说明参数也是放在data中,上面的博客应该是有错误的。

1.1 添加拦截器

跟Android的相关框架相似的,这里可以对网络添加拦截器,对请求数据进行前,后的相关操作。

  Dio getDio({String baseUrl}) {
    if (baseUrl == null) {
      baseUrl = KCWC;
    }
    dio = Dio(
      BaseOptions(
        baseUrl: baseUrl,
        connectTimeout: 5000,
        receiveTimeout: 5000,
        contentType: ContentType.parse(
          "application/x-www-form-urlencoded",
        ),
      ),
    );
    dio.interceptors.add(
      new InterceptorsWrapper(onRequest: (options) {
     ///请求数据前对数据进行统一的处理
        if (options.queryParameters != null) {
          options.queryParameters
              .addAll({"machine_type": Platform.operatingSystem});
        }
        if (options.data is Map<String, String>) {
          (options.data as Map<String, String>)
              .addAll({"machine_type": Platform.operatingSystem});
        }
        return options;
      }, onResponse: (response) {
       ///这个地方用来打印返回结果监听
        print(response);
        return response;
      }),
    );
    return dio;
  }

1.2 对网络请求状态进行管理

封装网络状态的相关状态,能反映网络状态,以及相关的数据处理结果。

/// 获取网络数据接口  前几个参数跟[Dio.request]一致。
  /// [T] 目标数据结构,基于项目是[BaseJson.data]中的数据结构。如果不关心返回数据,可以不用传入
  /// [start] 开始请求
  /// [end]  请求结束
  /// [succeed] 接口返回成功后返回对应的[BaseJson.data]数据,如果有指定[T]需要与[builder]配合使用。
  /// [failure] 数据错误的回调,配合[nullWord]来处理空数据的回调
  /// [builder] 如果指定了泛型T 需要传入对应的[builder]用来解析JSON。其中builder的参数根据具体的JSON数据,可能是list,可能是map
  /// [options] 为空默认用get,快捷方式[post],[delete]
  /// [responseCallBack] 直接获取相应的Response,只要有就会返回。与[succeed]不冲突
  /// [networkError] 相关的网络错误返回,详见:[NetWorkStatStatus]
  /// [ignoreStatusCode] 是否忽略状态码。为0表示成功。默认不忽略。
  request<T>(
    url, {
    data,
    Map<String, dynamic> queryParameters,
    CancelToken cancelToken,
    Options options,
    ProgressCallback onSendProgress,
    ProgressCallback onReceiveProgress,
    String baseUrl = KCWC,
    VoidCallback start,
    VoidCallback end,
    ValueChanged<Response> responseCallBack,
    DataBeanBuilder<T> builder,
    ValueChanged<T> succeed,
    ValueChanged<String> failure,
    String nullWord = "返回数据为空",
    ValueChanged<BaseJson> networkError,
    bool ignoreStatusCode = false,
  }) async {
    if (networkError == null) {
      networkError = defaultCallBack;
    }
    if (start == null) {
      start = () => defaultCallBack("start");
    }
    if (end == null) {
      end = () => defaultCallBack("end");
    }
    if (responseCallBack == null) {
      responseCallBack = (v) => defaultCallBack(v);
    }
    await start();
    if (url == null) {
      await end();
      return;
    }
    // 判断网络
    var connectivityResult = await (new Connectivity().checkConnectivity());
    if (connectivityResult == ConnectivityResult.none) {
      networkError(NetWorkStatStatus.NO_NET_WORK);
      await end();
      return;
    }
    Response response;
    try {
      response = await getDio(baseUrl: baseUrl).request(url,
          data: data,
          cancelToken: cancelToken,
          queryParameters: queryParameters,
          onSendProgress: onSendProgress,
          onReceiveProgress: onReceiveProgress,
          options: options);
    } on DioError catch (error) {
      await end();
      CommonUtils.log2(["请求异常", url, error.toString()]);
      switch (error.type) {
        case DioErrorType.CONNECT_TIMEOUT:
          networkError(NetWorkStatStatus.CONNECT_TIMEOUT);
          return;
        case DioErrorType.SEND_TIMEOUT:
          networkError(NetWorkStatStatus.SEND_TIMEOUT);
          return;
        case DioErrorType.RECEIVE_TIMEOUT:
          networkError(NetWorkStatStatus.RECEIVE_TIMEOUT);
          return;
        case DioErrorType.RESPONSE:
          networkError(NetWorkStatStatus.RESPONSE);
          return;
        case DioErrorType.CANCEL:
          networkError(NetWorkStatStatus.CANCEL);
          return;
        default:
          networkError(NetWorkStatStatus.DEFAULT);
          return;
      }
    }
    if (response == null) {
      await end();
      networkError(NetWorkStatStatus.NULL_BACK);
      return;
    }
    responseCallBack(response);
    await end();
    BaseJson<T> baseJson;
    try {
      baseJson = BaseJson<T>.fromJsonString(response.toString(), builder);
    } catch (e) {
      CommonUtils.log2([e]);
      if (failure != null) {
        failure(NetworkStatusCode.DATA_ERROR);
      }
      return;
    }
    BaseJson.checkAns(baseJson,
        succeed: succeed, failure: failure, nullWord: nullWord);
  }

  static Options post = Options(method: "POST");
  static Options put = Options(method: "PUT");
  static Options delete = Options(method: "DELETE");

2 对数据进行封装。

  1. json解析
  2. 数据模板

由于这里不支持反射,所以原本的那些框架都不能用,这个提供的方案其实都是本地自动生成相关的代码。
个人推荐使用网络模板生成,更简单,直观

2.1 json解析

2.1.1 方案1:使用官方的方式

这种方案显得很臃肿,引入一个个包不说还要命令生成。太麻烦。
git:283e705a6ce17da3f05c2ed58951501966b13fe6

2.1.1.1 加入相关的依赖 pubspec.yaml
dependencies:
  # Your other regular dependencies here
  json_annotation: ^2.0.0

dev_dependencies:
  # Your other dev_dependencies here
  build_runner: ^1.0.0
  json_serializable: ^2.0.0

2.1.1.2 自己写相关的原始类,预留申明 参考
2.1.1.3. 运行代码生成程序 参考
flutter packages pub run build_runner build

2.1.2 方案2 直接生成模板

直接用工具生成需要的代码,不必转几次。这里唯一不友好的是,这样不能用@JsonKey这种关键字,来起别名,但是这样无伤大雅。要别名手动改就行。而且,实际运用中并没有多少次用到了别名。

工具网站

2.1.2.1 由于无法泛型实例化,这里要手动解析下基类数据

只能将具体的数据放在data中,当string处理,然后进一步的解析json。参考

2.1.2.2 复杂的数据结构这个工具网站无法处理,也是需要自己去添加

2.2 数据模板

一般来讲,数据都会有一个统一的数据模板。其中 data中的数据才是具体内容,这里就需要一个通用模板

{
    "code": 1,
    "msg": "xxx",
    "data": ""
}

由于flutter把dart的反射给屏蔽了,我们也没必要强行使用反射。这里我们传入一个构造方法来当作构造器DataBeanBuilder。
并通过这个构造器来构成相应的数据类型。

class BaseJson<T> {
  int code;
  String msg;
  T data;

  BaseJson(this.code, this.msg, this.data);

  BaseJson.fromJson(Map<String, dynamic> json, DataBeanBuilder<T> builder) {
    code = json['code'];
    msg = json['msg'];
    if (builder != null) {
      data = builder(json['data']);
    } else {
      data = json['data'];
    }
  }

  BaseJson.fromJsonString(String jsonString, DataBeanBuilder<T> builder) {
    Map<String, dynamic> jsonMap = json.decode(jsonString);
    try {
      code = jsonMap['code'];
    } catch (e) {
      CommonUtils.log2([e]);
      code = -1;
    }

    msg = jsonMap['msg'];
    if (builder != null) {
      data = builder(jsonMap['data']);
    } else {
      data = jsonMap['data'];
    }
  }
}

/// 通过第一步解析[BaseJson]得到的data的数据来再次解析成对应的数据,[value]为list或者map
typedef DataBeanBuilder<T> = T Function(dynamic value);

///当构建的DATA是list的时候用
List<T> build<T>(dynamic d, DataBeanBuilder<T> builder) {
  if (d != null && d is List) {
    return d.map((v) => builder(v)).toList();
  }
  return [];
}

范例

  getAllSetList(
      {ValueChanged<List<AllocationSetListItemBean>> succeed,
      VoidCallback start,
      VoidCallback end,
      ValueChanged<String> failure,
      ValueChanged<BaseJson> networkError,
      String nullWord}) async {
    String testUrl = "http://ya7/v1/cluesalesman?token=xxx";

///这里的builder中其实要得只是list的数据,所有,就直接在这里复写。
    DataBeanBuilder<List<AllocationSetListItemBean>> builder = (data) {
      if (data == null || data["list"] is! List) return [];
      return (data["list"] as List).map((v) {
        return new AllocationSetListItemBean.fromJson(v);
      }).toList();
    };

    DioHelper.getInstance().request<List<AllocationSetListItemBean>>(
      Test.testUrl ? testUrl : url,
      queryParameters: Test.testUrl ? null : {"token": token},
      start: start,
      end: end,
      succeed: succeed,
      failure: failure,
      nullWord: nullWord,
      networkError: networkError,
      builder: builder,
    );
  }

相关文章

网友评论

      本文标题:Flutter网络框架

      本文链接:https://www.haomeiwen.com/subject/qdugfhtx.html