Dio分析-Transform

概述

Dio的HttpClientAdapter实现Http网络库的定制;Interceptor实现网络交互过程的拦截处理器;Transform实现有关请求和响应的编码转换过程。

设计

转换包括写入流的数据转换和读取流的数据转换

请求编码

请求编码,针对的是requestOptions.data, 主要包括请求参数的数据编码(jsonEncode)和请求体的数据写入(stream)的数据流生成。

BackgroundTransformer

请求参数的编码使用的是BackgroundTransformer,特点就是后台并发,缺省使用的是(jsonEncode)
BackgroundTransformer的设计结构如下图
在这里插入图片描述

@override
  Transformer transformer = BackgroundTransformer();

BackgroundTransformer继承于SyncTransformer,同步转换器,使用JsonEncode和JsonDecode作为基础的编码和解码的工具类。提供transformRequest和transformResponse实现请求和响应内容的编解码。
有趣的一点是BackgroundTransformer,后台同步转换器,使用_decodeJson方法替代父类的jsondecode,而其中使用Dart的computer机制实现针对超过50k数据,创建一个独立的isolate实现解码,避免主线程的打断。

Transformer抽象类主要限定转换器的行为,包括transformRequest/transformResponse,以及一些通用的方法实现 urlEncodeMap等。

FutureOr<dynamic> _decodeJson(String text) {
  // Taken from https://github.com/flutter/flutter/blob/135454af32477f815a7525073027a3ff9eff1bfd/packages/flutter/lib/src/services/asset_bundle.dart#L87-L93
  // 50 KB of data should take 2-3 ms to parse on a Moto G4, and about 400 μs
  // on a Pixel 4.
  if (text.codeUnits.length < 50 * 1024) {
    return jsonDecode(text);
  }
  // For strings larger than 50 KB, run the computation in an isolate to
  // avoid causing main thread jank.
  return compute(jsonDecode, text);
}

compute使用

请求体stream写入流转换

请求体写入通过_transformData生成写入流并传入httpClientAdapter作为请求体的数据流。

      final stream = await _transformData(reqOpt);
      final responseBody = await httpClientAdapter.fetch(
        reqOpt,
        stream,
        cancelToken?.whenCancel,
      );

要了解stream写入方案,首先需要了解stream异步编程的基础知识

Stream异步编程

首先要学习一下Dart异步编程stream的才能正确理解transform的处理逻辑
异步编程stream使用

Stream 提供一个异步的数据序列。
数据序列包括用户生成的事件和从文件读取的数据。
你可以使用 Stream API 中的 listen() 方法和 await for 关键字来处理一个 Stream。
当出现错误时,Stream 提供一种处理错误的方式。
Stream 有两种类型:Single-Subscription 和 Broadcast。

异步编程stream创建

你可以通过以下几种方式创建 Stream。
转换现有的 Stream。
使用 async* 函数创建 Stream。
使用 StreamController 生成 Stream。

Stream, StreamController, StreamTransform

StreamController

针对Dart stream的异步编程,需要了解streamController, streamController中最重要的两个参数stream和sink, stream用于异步读取数据队列,sink用于控制数据生成(streamSink)。

abstract interface class StreamController<T> implements StreamSink<T> {
  /// The stream that this controller is controlling.
  Stream<T> get stream;
  StreamSink<T> get sink;

同时支持onListen, onPause, onResume, onCancel

/// The callback which is called when the stream is listened to.
  ///
  /// May be set to `null`, in which case no callback will happen.
  abstract void Function()? onListen;

  /// The callback which is called when the stream is paused.
  ///
  /// May be set to `null`, in which case no callback will happen.
  ///
  /// Pause related callbacks are not supported on broadcast stream controllers.
  abstract void Function()? onPause;

  /// The callback which is called when the stream is resumed.
  ///
  /// May be set to `null`, in which case no callback will happen.
  ///
  /// Pause related callbacks are not supported on broadcast stream controllers.
  abstract void Function()? onResume;

  /// The callback which is called when the stream is canceled.
  ///
  /// May be set to `null`, in which case no callback will happen.
  abstract FutureOr<void> Function()? onCancel;

使用案例

import 'dart:async';  
  
void main() {  
  // 声明一个 StreamController  
  StreamController controller = StreamController();  
  
  // 监听此 Stream  
  StreamSubscription subscription1 =  
      controller.stream.listen((value) => print('$value'));  
  
  // 往 Stream 中添加数据  
  controller.sink.add(0);  
  controller.sink.add('a, b, c, d');  
  controller.sink.add(3.14);  
  
  // 关闭 StreamController  
  controller.close();  
}
StreamSubscription

订阅者的令牌,由stream.listen产生提供订阅者进行过程控制.

// 监听此 Stream  
  StreamSubscription subscription1 =  
      controller.stream.listen((value) => print('$value'));

控制stream数据生成过程,cancel, onData,onError,onDone,pause,resume,

StreamTransformer

StreamTransformer是用来对stream数据进行自定义转换的逻辑,主要利用StreamController实现相关数据的生成转换逻辑,

自定义StreamTransformer

自定义的StreamTransfomer

/// 自定义一个 StreamTransformer ,
/// 泛型类型 S 为入参类型,T 为出参类型
/// 这些类型都是 Stream 中传递的数据类型
class MyTransformer<S, T> implements StreamTransformer<S, T> {

  // 用来生成一个新的 Stream 并且控制符合条件的数据
  StreamController _controller;

  StreamSubscription _subscription;

  bool cancelOrError;

  // 转换之前的 Stream
  Stream<S> _stream;

  MyTransformer({bool sync: false, this.cancelOrError}) {
    _controller = new StreamController<T>(
        onListen: _onListen,
        onCancel: _onCancel,
        onPause: () {
          _subscription.pause();
        },
        onResume: () {
          _subscription.resume();
        },
        sync: sync);
  }

  MyTransformer.broadcast({bool sync: false, bool this.cancelOrError}) {
    // 定义一个 StreamController,注意泛型类型为 T,也就是出参类型,因为
    // 我们是使用该 _controller 生成一个用来返回的新的 Stream<T>
    _controller = new StreamController<T>.broadcast(
        onListen: _onListen, onCancel: _onCancel, sync: sync);
  }

  void _onListen() {
    // _stream 为转换之前的 Stream<S>
    _subscription = _stream.listen(onData,
        onError: _controller.addError,
        onDone: _controller.close,
        cancelOnError: cancelOrError);
  }

  void _onCancel() {
    _subscription.cancel();
    _subscription = null;
  }

  // 数据转换
  void onData(S data) {
    if ((data as int) % 2 == 0) {
      // 将符合条件的数据添加到新的 Stream 中
      _controller.sink.add(data);
    }
  }

  // 参数为转换之前的 Stream<S>
  // 返回的是一个新的 Stream<T> (转换之后的 Stream)
  @override
  Stream<T> bind(Stream<S> stream) {
    this._stream = stream;
    return _controller.stream;
  }

  @override
  StreamTransformer<RS, RT> cast<RS, RT>() {
    // TODO: implement cast
    return null;
  }
}
bind与stream进行绑定

利用stream.transform()实现有关流和自定义流转换器的绑定

Stream<S> transform<S>(StreamTransformer<T, S> streamTransformer) {
    return streamTransformer.bind(this);
  }
FromHandler行为封装

通常使用FromHandler来封装转换行为,避免使用自定义的StreamTransform

abstract interface class StreamTransformer<S, T> {
factory StreamTransformer.fromHandlers(
      {void handleData(S data, EventSink<T> sink)?,
      void handleError(Object error, StackTrace stackTrace, EventSink<T> sink)?,
      void handleDone(EventSink<T> sink)?}) = _StreamHandlerTransformer<S, T>;

}

请求处理

request option的数据处理通过_transformData来实现相关数据的转换逻辑,针对不同option.data进行数据编码,并变更请求头的contentLength的长度。

Future<Stream<Uint8List>?> _transformData(RequestOptions options) async

根据options.data的类型,统一转换成stream,如果是FormData,通过FormData.finalize()获得stream, 如果是Uint8List或者使用请求options进行backgroundTransfomer进行编码后通过Stream.fromIterable生成stream。
stream流通过bind与streamTransformer关联

Stream<Uint8List> addProgress(
  Stream<List<int>> stream,
  int? length,
  RequestOptions options,
) {
  final streamTransformer = stream is Stream<Uint8List>
      ? _transform<Uint8List>(stream, length, options)
      : _transform<List<int>>(stream, length, options);
  return stream.transform<Uint8List>(streamTransformer);
}

流处理逻辑,通过streamTransformer实现有关流的绑定和进度处理。通过工厂方法fromHandlers,将handleData handleError handleDone这些方法封装生成streamTransformer与stream进行绑定。

factory StreamTransformer.fromHandlers(
      {void handleData(S data, EventSink<T> sink)?,
      void handleError(Object error, StackTrace stackTrace, EventSink<T> sink)?,
      void handleDone(EventSink<T> sink)?}) = _StreamHandlerTransformer<S, T>;

以下代码,标准的streamTransformer.handleData通过sink进行数据异步处理。同时通过options.onSendProgress实现进度报告。

Stream<Uint8List> addProgress(
  Stream<List<int>> stream,
  int? length,
  RequestOptions options,
) {
  final streamTransformer = stream is Stream<Uint8List>
      ? _transform<Uint8List>(stream, length, options)
      : _transform<List<int>>(stream, length, options);
  return stream.transform<Uint8List>(streamTransformer);
}

StreamTransformer<S, Uint8List> _transform<S extends List<int>>(
  Stream<S> stream,
  int? length,
  RequestOptions options,
) {
  int complete = 0;
  return StreamTransformer<S, Uint8List>.fromHandlers(
    handleData: (S data, sink) {
      final cancelToken = options.cancelToken;
      if (cancelToken != null && cancelToken.isCancelled) {
        cancelToken.requestOptions = options;
        sink
          ..addError(cancelToken.cancelError!)
          ..close();
      } else {
        if (data is Uint8List) {
          sink.add(data);
        } else {
          sink.add(Uint8List.fromList(data));
        }
        if (length != null) {
          complete += data.length;
          if (options.onSendProgress != null) {
            options.onSendProgress!(complete, length);
          }
        }
      }
    },
  );
}

响应处理

响应处理涉及响应流的读取和响应数据解码,响应流针对的是responsbody.stream读取,使用的是stream ,streamController,subscripition的异步处理方法

Stream<Uint8List> handleResponseStream(
  RequestOptions options,
  ResponseBody response, {
  @visibleForTesting void Function()? onReceiveTimeoutWatchCancelled,
}) {
  final source = response.stream;
  final responseSink = StreamController<Uint8List>();
  late StreamSubscription<List<int>> responseSubscription;

}

responseSink作为stream数据读取的容器,主要目的是添加stream读取进度和控制读取超时的逻辑.responseSubscription用于stream读取的流程控制。

responseSubscription = source.listen(
    (data) {
      watchReceiveTimeout();
      // Always true if the receive timeout was not set.
      if (receiveStopwatch.elapsed <= receiveTimeout) {
        responseSink.add(data);
        options.onReceiveProgress?.call(
          receivedLength += data.length,
          totalLength,
        );
      }
    },
    onError: (error, stackTrace) {
      stopWatchReceiveTimeout();
      responseSink.addErrorAndClose(error, stackTrace);
    },
    onDone: () {
      stopWatchReceiveTimeout();
      responseSubscription.cancel();
      responseSink.close();
    },
    cancelOnError: true,
  );

这一段代码使用了stopWatch秒表的实现逻辑,用于接收数据的时间控制

final receiveStopwatch = Stopwatch();
 if (receiveStopwatch.elapsed <= receiveTimeout) {

用于控制单次stream数据接收流程的时间秒表计数.

根据requestOption.ResponseType确定解码结果,.Stream直接返回responsebody.stream, .bytes返回uInt8序列,.json返回backgroudnTransformer jsonDecode的结果.

  • 14
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值