美文网首页
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