概述
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);
}
请求体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。
使用 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的结果.