文章系列
Flutter Dio源码剖析(一)--Dio介绍
Flutter Dio源码剖析(二)--HttpClient、Http、Dio比照
Flutter Dio源码剖析(三)--深度分析
Flutter Dio源码剖析(四)--封装
视频系列
Flutter Dio源码剖析(一)--Dio介绍视频教程
Flutter Dio源码剖析(二)--HttpClient、Http、Dio比照视频教程
Flutter Dio源码剖析(三)--深度分析视频教程
Flutter Dio源码剖析(四)--封装视频教程
源码仓库地址
github仓库地址
介绍
在后面两篇文章中咱们说了Dio
的介绍以及对HttpClient
、Http
、Dio
这三个网络申请的剖析,这章节次要是对Dio
源码的剖析。
从post申请来进行剖析
var response = await Dio().post('http://localhost:8080/login', queryParameters: { "username": "123456", "password": "123456"});
post办法
post
办法有七个参数,在该函数中调用了request
办法,并没有做任何解决,接下来咱们看下request
办法。
- path: 申请的url链接
- data: 申请数据,例如上传用到的FromData
- queryParameters: 查问参数
- options: 申请选项
- cancelToken: 用来勾销发送申请的token
- onSendProgress: 网络申请发送的进度
- onReceiveProgress: 网络申请接管的进度
@overrideFuture<Response<T>> post<T>( String path, { data, Map<String, dynamic>? queryParameters, Options? options, CancelToken? cancelToken, ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress,}) { return request<T>( path, data: data, options: checkOptions('POST', options), queryParameters: queryParameters, cancelToken: cancelToken, onSendProgress: onSendProgress, onReceiveProgress: onReceiveProgress, );}
request办法
request
接管了post
办法中传进来的参数。
第一步:合并选项
通过调用compose
办法来进行选项合并。
compose函数执行流程
- 首先判断
queryParameters
是否为空,不为空则增加到一个query
长期变量中 - 把
options
中的headers
全副拿进去存到长期变量_headers
中进行不辨别大小写的映射,并删除headers
中的contentTypeHeader
- 如果
headers
不为空,则把headers
中的全副属性增加到长期变量_headers
中并把contentTypeHeader
赋值到一个长期变量_contentType
中。 - 把
options
中的自定义字段extra
赋值给一个长期变量 - 把
method
对立转换成大写字母 - 创立一个
RequestOptions
并传入下面解决过的参数并返回
compose源码
RequestOptions compose( BaseOptions baseOpt, String path, { data, Map<String, dynamic>? queryParameters, CancelToken? cancelToken, Options? options, ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) { var query = <String, dynamic>{}; if (queryParameters != null) query.addAll(queryParameters); query.addAll(baseOpt.queryParameters); var _headers = caseInsensitiveKeyMap(baseOpt.headers); _headers.remove(Headers.contentTypeHeader); var _contentType; if (headers != null) { _headers.addAll(headers!); _contentType = _headers[Headers.contentTypeHeader]; } var _extra = Map<String, dynamic>.from(baseOpt.extra); if (extra != null) { _extra.addAll(extra!); } var _method = (method ?? baseOpt.method).toUpperCase(); var requestOptions = RequestOptions( method: _method, headers: _headers, extra: _extra, baseUrl: baseOpt.baseUrl, path: path, data: data, connectTimeout: baseOpt.connectTimeout, sendTimeout: sendTimeout ?? baseOpt.sendTimeout, receiveTimeout: receiveTimeout ?? baseOpt.receiveTimeout, responseType: responseType ?? baseOpt.responseType, validateStatus: validateStatus ?? baseOpt.validateStatus, receiveDataWhenStatusError: receiveDataWhenStatusError ?? baseOpt.receiveDataWhenStatusError, followRedirects: followRedirects ?? baseOpt.followRedirects, maxRedirects: maxRedirects ?? baseOpt.maxRedirects, queryParameters: query, requestEncoder: requestEncoder ?? baseOpt.requestEncoder, responseDecoder: responseDecoder ?? baseOpt.responseDecoder, listFormat: listFormat ?? baseOpt.listFormat, ); requestOptions.onReceiveProgress = onReceiveProgress; requestOptions.onSendProgress = onSendProgress; requestOptions.cancelToken = cancelToken; requestOptions.contentType = _contentType ?? contentType ?? baseOpt.contentTypeWithRequestBody(_method); return requestOptions;}
第二步:调用fetch
判断用户是否敞开申请,敞开则退出,未敞开调用Fetch办法
request源码
@override Future<Response<T>> request<T>( String path, { data, Map<String, dynamic>? queryParameters, CancelToken? cancelToken, Options? options, ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { options ??= Options(); var requestOptions = options.compose( this.options, path, data: data, queryParameters: queryParameters, onReceiveProgress: onReceiveProgress, onSendProgress: onSendProgress, cancelToken: cancelToken, ); requestOptions.onReceiveProgress = onReceiveProgress; requestOptions.onSendProgress = onSendProgress; requestOptions.cancelToken = cancelToken; if (_closed) { throw DioError( requestOptions: requestOptions, error: "Dio can't establish new connection after closed.", ); } return fetch<T>(requestOptions); }
Fetch办法
第一步:申请参数赋值
判断如果传递进来的requestOptions.cancelToken
不为空的状况下,则把传递进来的requestOptions
进行赋值。
if (requestOptions.cancelToken != null) { requestOptions.cancelToken!.requestOptions = requestOptions;}
第二步:响应数据设定
如果申请回来的参数不是动静类型并且不是bytes
和stream
的形式,则进行判断该返回值类型是否是字符串,为真返回UTF-8的编码类型,否则返回字符串类型
if (T != dynamic && !(requestOptions.responseType == ResponseType.bytes || requestOptions.responseType == ResponseType.stream)) { if (T == String) { requestOptions.responseType = ResponseType.plain; } else { requestOptions.responseType = ResponseType.json; }}
第三步:构建申请流并增加拦截器
1、构建一个申请流,InterceptorState
是一个外部类,外面总共与两个属性T data
以及 InterceptorResultType type
,用于以后拦截器和下一个拦截器之间传递状态所定义。
2、按 FIFO 程序执行,循环遍历向申请流中增加申请拦截器,拦截器中最次要的有RequestInterceptor
申请前拦挡和 ResponseInterceptor
申请后拦挡的两个实例。
var future = Future<dynamic>(() => InterceptorState(requestOptions));interceptors.forEach((Interceptor interceptor) { future = future.then(_requestInterceptorWrapper(interceptor.onRequest));});
第四步:拦截器转换为函数回调
这里次要做的一步操作是把函数的回调作为办法的参数,这样就实现了把拦截器转换为函数回调,这里做了一层判断,如果state.type
等于 next
的话,那么会减少一个监听勾销的异步工作,并把cancelToken
传递给了这个工作,接下来他会查看以后的这个拦截器申请是否入队,最初定义了一个申请拦截器的变量,该拦截器外面有三个次要的办法别离是next
() 、resole()
、 reject()
,最初把这个拦截器返回进来。
FutureOr Function(dynamic) _requestInterceptorWrapper( void Function( RequestOptions options, RequestInterceptorHandler handler, ) interceptor,) { return (dynamic _state) async { var state = _state as InterceptorState; if (state.type == InterceptorResultType.next) { return listenCancelForAsyncTask( requestOptions.cancelToken, Future(() { return checkIfNeedEnqueue(interceptors.requestLock, () { var requestHandler = RequestInterceptorHandler(); interceptor(state.data, requestHandler); return requestHandler.future; }); }), ); } else { return state; } };}
第五步:构建申请流调度回调
调度回调和增加拦截器转换为函数回调,不同的是调度回调外面进行了申请散发。
future = future.then(_requestInterceptorWrapper(( RequestOptions reqOpt, RequestInterceptorHandler handler,) { requestOptions = reqOpt; _dispatchRequest(reqOpt).then( (value) => handler.resolve(value, true), onError: (e) { handler.reject(e, true); }, );}));
第六步:申请散发
1、申请散发函数外面会调用_transfromData
进行数据转换,最终转换进去的数据是一个 Stream
流。
2、调用网络申请适配器进行网络申请 fetch
办法,这里阐明下该适配器定义有两个,别离如下:
2.1、BrowserHttpClientAdapter
是调用了html_dart2js
的库进行了网络申请,该库是将dart
代码编译成可部署的JavaScript
2.2、DefaultHttpClientAdapter
是采纳零碎申请库HttpClient
进行网络申请。
3、把响应头赋值给长期变量responseBody
并通过fromMap
转换成 Map<String, List<String>>
类型
4、初始化响应类,并对返回的数据进行赋值解决。
5、判断如果是失常返回就对ret.data
变量进行数据格式转换,失败则勾销监听响应流
6、查看申请是否通过cancelToken
变量勾销了,如果勾销了则间接抛出异样
7、最初在进行申请是否失常,如果失常则查看是否入队并返回,否则间接抛出申请异样DioError
Future<Response<T>> _dispatchRequest<T>(RequestOptions reqOpt) async { var cancelToken = reqOpt.cancelToken; ResponseBody responseBody; try { var stream = await _transformData(reqOpt); responseBody = await httpClientAdapter.fetch( reqOpt, stream, cancelToken?.whenCancel, ); responseBody.headers = responseBody.headers; var headers = Headers.fromMap(responseBody.headers); var ret = Response( headers: headers, requestOptions: reqOpt, redirects: responseBody.redirects ?? [], isRedirect: responseBody.isRedirect, statusCode: responseBody.statusCode, statusMessage: responseBody.statusMessage, extra: responseBody.extra, ); var statusOk = reqOpt.validateStatus(responseBody.statusCode); if (statusOk || reqOpt.receiveDataWhenStatusError == true) { var forceConvert = !(T == dynamic || T == String) && !(reqOpt.responseType == ResponseType.bytes || reqOpt.responseType == ResponseType.stream); String? contentType; if (forceConvert) { contentType = headers.value(Headers.contentTypeHeader); headers.set(Headers.contentTypeHeader, Headers.jsonContentType); } ret.data = await transformer.transformResponse(reqOpt, responseBody); if (forceConvert) { headers.set(Headers.contentTypeHeader, contentType); } } else { await responseBody.stream.listen(null).cancel(); } checkCancelled(cancelToken); if (statusOk) { return checkIfNeedEnqueue(interceptors.responseLock, () => ret) as Response<T>; } else { throw DioError( requestOptions: reqOpt, response: ret, error: 'Http status error [${responseBody.statusCode}]', type: DioErrorType.response, ); } } catch (e) { throw assureDioError(e, reqOpt); }}
download办法
download
办法的执行流程和post
一样,只是接管的数据类型以及逻辑解决上不一样,会把下载的文件保留到本地,具体实现流程在 src>entry>dio_fornative.dart 文件中,这里不在做过多的赘述。
总结
在咱们进行 get()
post()
等调用时,都会进入到request
办法,request
办法次要负责对申请参数以及自定义申请头的对立解决,并调用了fetch
办法,而 fetch
中是对响应数据设定、构建申请流、增加拦截器、申请散发的操作。