Flutter网络请求库DIO入门文档,重磅

7、拦截器中可以进行其他异步操作

dio.interceptors.add(InterceptorsWrapper(
onRequest:(Options options) async{
//…If no token, request token firstly.
Response response = await dio.get(“/token”);
//Set the token to headers
options.headers[“token”] = response.data[“data”][“token”];
return options; //continue
}
));

8、Lock/unlock拦截器

你可以通过调用拦截器的 lock()/unlock 方法来锁定/解锁拦截器。一旦请求/响应拦截器被锁定,接下来的请求/响应将会在进入请求/响应拦截器之前排队等待,直到解锁后,这些入队的请求才会继续执行(进入拦截器)。这在一些需要串行化请求/响应的场景中非常实用,后面我们将给出一个示例。
tokenDio = Dio(); //Create a instance to request the token.
tokenDio.options = dio.options;
dio.interceptors.add(InterceptorsWrapper(
onRequest:(Options options) async {
// If no token, request token firstly and lock this interceptor
// to prevent other request enter this interceptor.
dio.interceptors.requestLock.lock();
// We use a Dio(to avoid dead lock) instance to request token.
Response response = await tokenDio.get(“/token”);
//Set the token to headers
options.headers[“token”] = response.data[“data”][“token”];
dio.interceptors.requestLock.unlock();
return options; //continue
}
));
假设这么一个场景:我们需要给每个请求头中设置token,如果token不存在我们需要先请求token,获取到再继续请求,由于请求token过程是异步的,所以我们需要先锁定拦截器防止其他请求在没有获取到token的情况下进行网络请求,获取到token再解锁

9、clear()方法来清空等待队列

dio.interceptors.clear()

10、日志(开启后会打印request和response相关信息)

//由于拦截器队列的执行顺序是FIFO,如果把log拦截器添加到了最前面,则后面拦截器对options的更改就不会被打印(但依然会生效), 所以建议把log拦截添加到队尾。
dio.interceptors.add(LogInterceptor(responseBody: false)); //开启请求日志

11、DioError

{
/// Request info.
RequestOptions request;

/// Response info, it may be null if the request can’t reach to
/// the http server, for example, occurring a dns error, network is not available.
Response response;

/// 错误类型,见下文
DioErrorType type;

///原始的error或exception对象,通常type为DEFAULT时存在。
dynamic error;
}

enum DioErrorType {
/// It occurs when url is opened timeout.
CONNECT_TIMEOUT,

/// It occurs when url is sent timeout.
SEND_TIMEOUT,

///It occurs when receiving timeout.
RECEIVE_TIMEOUT,

/// When the server response, but with a incorrect status, such as 404, 503…
RESPONSE,

/// When the request is cancelled, dio will throw a error with this type.
CANCEL,

/// Default error type, Some other Error. In this case, you can
/// read the DioError.error if it is not null.
DEFAULT
}

12、CancelToken,取消请求

CancelToken token = CancelToken();
dio.post(“/testpost?id=1&name=dio1&method=post”,cancelToken: token).catchError((e) {
//我们会发现CancelToken提供了错误类型的判断,即此时CancelToken.isCancel(err)是true
//如
if (CancelToken.isCancel(err)) {
print(“被取消啦”);
}

}).then((data) {
return data;
});
token.cancel();

cancel_token.dart中源码也是判断DioErrorType
static bool isCancel(DioError e) {
return e.type == DioErrorType.CANCEL;
}

13、dio和HttpClient关系

HttpClientAdapter是 Dio 和 HttpClient之间的桥梁。2.0抽象出adapter主要是方便切换、定制底层网络库。Dio实现了一套标准的、强大API,而HttpClient则是真正发起Http请求的对象。我们通过HttpClientAdapter将Dio和HttpClient解耦,这样一来便可以自由定制Http请求的底层实现,比如,在Flutter中我们可以通过自定义HttpClientAdapter将Http请求转发到Native中,然后再由Native统一发起请求。再比如,假如有一天OKHttp提供了dart版,你想使用OKHttp发起http请求,那么你便可以通过适配器来无缝切换到OKHttp,而不用改之前的代码。 Dio 使用DefaultHttpClientAdapter作为其默认HttpClientAdapter,DefaultHttpClientAdapter使用dart:io:HttpClient 来发起网络请求。

扩展(适配器模式) 首页定义接口,接口中对要实现的功能加以抽象,然后定义不同的Adapter类来实现这个接口,Adapter类中是对接口中方法的不同实现,上层的调用代码不需要改变就可以随意切换对底层不同的功能调用。

14、设置代理

DefaultHttpClientAdapter 提供了一个onHttpClientCreate 回调来设置底层 HttpClient的代理,我们想使用代理,可以参考下面代码:
import ‘package:dio/dio.dart’;
import ‘package:dio/adapter.dart’;

(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
// config the http client
client.findProxy = (uri) {
//proxy all request to localhost:8888
return “PROXY localhost:8888”;
};
// you can also create a HttpClient to dio
// return HttpClient();
};

15、部分源码分析

dio.dart中

网络请求最终会调用到_request方法
当Response的泛型类为String且声明的ResponseType不为bytes和stream时
mergeOptions是将Dio的BaseOptions属性结合请求参数Options来生成一个RequestOptions对象,是最终发起网络请求的Options
Future<Response> _request(
String path, {
data,
Map<String, dynamic> queryParameters,
CancelToken cancelToken,
Options options,
ProgressCallback onSendProgress,
ProgressCallback onReceiveProgress,
}) async {
if (_closed) {
throw DioError(error: “Dio can’t establish new connection after closed.”);
}
options ??= Options();
if (options is RequestOptions) {
data = data ?? options.data;
queryParameters = queryParameters ?? options.queryParameters;
cancelToken = cancelToken ?? options.cancelToken;
onSendProgress = onSendProgress ?? options.onSendProgress;
onReceiveProgress = onReceiveProgress ?? options.onReceiveProgress;
}
var requestOptions = mergeOptions(options, path, data, queryParameters);
requestOptions.onReceiveProgress = onReceiveProgress;
requestOptions.onSendProgress = onSendProgress;
requestOptions.cancelToken = cancelToken;
if (T != dynamic &&
!(requestOptions.responseType == ResponseType.bytes ||
requestOptions.responseType == ResponseType.stream)) {
if (T == String) {
requestOptions.responseType = ResponseType.plain;
} else {
requestOptions.responseType = ResponseType.json;
}
}

拦截器会判断
checkIfNeedEnqueue方法的作用就是判断是否有未处理完(判断是否处理完从而加锁是通过Completer实现的)的请求,如果有的话,本次请求需要排队等待之前的请求的完成(值得学习的是由于一次请求返回的是Future,所以这里利用了future.then((Callback))返回的还是一个Future对象的特点,巧妙的实现了多次请求的顺序执行而相互之间不会干扰),这里的then中的Callback回调就是checkIfNeedEnqueue的第二个参数。

// Convert the request/response interceptor to a functional callback in which
// we can handle the return value of interceptor callback.
Function _interceptorWrapper(interceptor, bool request) {
return (data) async {
var type = request ? (data is RequestOptions) : (data is Response);
var lock =
request ? interceptors.requestLock : interceptors.responseLock;
if (_isErrorOrException(data) || type) {
return listenCancelForAsyncTask(
cancelToken,
Future(() {
return checkIfNeedEnqueue(lock, () {
if (type) {
if (!request) data.request = data.request ?? requestOptions;
return interceptor(data).then((e) => e ?? data);
} else {
throw assureDioError(data, requestOptions);
}
});
}),
);
} else {
return assureResponse(data, requestOptions);
}
};
}

//如果拦截器被锁,接下来的request/response任务会进入一个队列,否则继续执行

FutureOr checkIfNeedEnqueue(Lock lock, EnqueueCallback callback) {
if (lock.locked) {
return lock.enqueue(callback);
} else {
return callback();
}
}

Future enqueue(EnqueueCallback callback) {
if (locked) {
// we use a future as a queue
return _lock.then((d) => callback());
}
return null;
}

//真正的网络请求,使用httpClientAdapter
//请求结果拿到后会查看interceptors.responseLock
//会监听是否有取消操作
// Initiate Http requests
Future<Response> _dispatchRequest(RequestOptions options) async {
var cancelToken = options.cancelToken;
ResponseBody responseBody;
try {
var stream = await _transformData(options);
responseBody = await httpClientAdapter.fetch(
options,
stream,
cancelToken?.whenCancel,
);
responseBody.headers = responseBody.headers ?? {};
var headers = Headers.fromMap(responseBody.headers ?? {});
var ret = Response(
headers: headers,
request: options,
redirects: responseBody.redirects ?? [],
isRedirect: responseBody.isRedirect,
statusCode: responseBody.statusCode,
statusMessage: responseBody.statusMessage,
extra: responseBody.extra,
);
var statusOk = options.validateStatus(responseBody.statusCode);
if (statusOk || options.receiveDataWhenStatusError) {
var forceConvert = !(T == dynamic || T == String) &&
!(options.responseType == ResponseType.bytes ||
options.responseType == ResponseType.stream);
String contentType;
if (forceConvert) {
contentType = headers.value(Headers.contentTypeHeader);
headers.set(Headers.contentTypeHeader, Headers.jsonContentType);
}
ret.data = await transformer.transformResponse(options, responseBody);
if (forceConvert) {
headers.set(Headers.contentTypeHeader, contentType);
}
} else {
await responseBody.stream.listen(null).cancel();
}
checkCancelled(cancelToken);
if (statusOk) {
return checkIfNeedEnqueue(interceptors.responseLock, () => ret);
} else {
throw DioError(
response: ret,
error: ‘Http status error [${responseBody.statusCode}]’,
type: DioErrorType.RESPONSE,
);
}
} catch (e) {
throw assureDioError(e, options);
}
}

//在io_adapter中
//dio中默认的adapter是DefaultHttpClientAdapter,其中HttpClient _defaultHttpClient,所以dio中的网络请求与底层HttpClient是通过DefaultHttpClientAdapter中对HttpClient网络请求的实现过程

class DefaultHttpClientAdapter implements HttpClientAdapter {
/// [Dio] will create HttpClient when it is needed.
/// If [onHttpClientCreate] is provided, [Dio] will call
/// it when a HttpClient created.
OnHttpClientCreate onHttpClientCreate;

HttpClient _defaultHttpClient;

bool _closed = false;

@override

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

最后

希望大家能有一个好心态,想进什么样的公司要想清楚,并不一定是大公司,我选的也不是特大厂。当然如果你不知道选或是没有规划,那就选大公司!希望我们能先选好想去的公司再投或内推,而不是有一个公司要我我就去!还有就是不要害怕,也不要有压力,平常心对待就行,但准备要充足。最后希望大家都能拿到一份满意的 offer !如果目前有一份工作也请好好珍惜好好努力,找工作其实挺累挺辛苦的。

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。可以点击GitHub免费获取

好想去的公司再投或内推,而不是有一个公司要我我就去!还有就是不要害怕,也不要有压力,平常心对待就行,但准备要充足。最后希望大家都能拿到一份满意的 offer !如果目前有一份工作也请好好珍惜好好努力,找工作其实挺累挺辛苦的。

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。可以点击GitHub免费获取

[外链图片转存中…(img-dksjItAS-1710837912293)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值