Flutter网络请求库DIO入门文档,成功拿下猿辅导+斗鱼+滴滴+字节+腾讯offer

/// Http请求头.
Map<String, dynamic> headers;

/// 连接服务器超时时间,单位是毫秒.
int connectTimeout;
/// 2.x中为接收数据的最长时限.
int receiveTimeout;

/// 请求路径,如果 path 以 "http(s)"开始, 则 baseURL 会被忽略; 否则,
/// 将会和baseUrl拼接出完整的的url.
String path = “”;

/// 请求的Content-Type,默认值是"application/json; charset=utf-8".
/// 如果您想以"application/x-www-form-urlencoded"格式编码请求数据,
/// 可以设置此选项为 Headers.formUrlEncodedContentType, 这样[Dio]
/// 就会自动编码请求体.
String contentType;

/// [responseType] 表示期望以那种格式(方式)接受响应数据。
/// 目前 [ResponseType] 接受三种类型 JSON, STREAM, PLAIN.
///
/// 默认值是 JSON, 当响应头中content-type为"application/json"时,dio 会自动将响应内容转化为json对象。
/// 如果想以二进制方式接受响应数据,如下载一个二进制文件,那么可以使用 STREAM.
///
/// 如果想以文本(字符串)格式接收响应数据,请使用 PLAIN.
ResponseType responseType;

/// validateStatus 决定http响应状态码是否被dio视为请求成功, 返回validateStatus
/// 返回true , 请求结果就会按成功处理,否则会按失败处理.
ValidateStatus validateStatus;

/// 用户自定义字段,可以在 [Interceptor]、[Transformer] 和 [Response] 中取到.
Map<String, dynamic> extra;

/// Common query parameters
Map<String, dynamic /String|Iterable/ > queryParameters;
}

5、响应数据

当请求成功时会返回一个Response对象,它包含如下字段:
{
/// 响应数据,可能已经被转换了类型, 详情请参考Options中的[ResponseType].
T data;
/// 响应头
Headers headers;
/// 本次请求信息
Options request;
/// Http status code.
int statusCode;
/// 是否重定向(Flutter Web不可用)
bool isRedirect;
/// 重定向信息(Flutter Web不可用)
List redirects ;
/// 真正请求的url(重定向最终的uri)
Uri realUri;
/// 响应对象的自定义字段(可以在拦截器中设置它),调用方可以在then中获取.
Map<String, dynamic> extra;
}

6、拦截器

我们可以通过继承Interceptor来实现自定义的拦截器

每个 Dio 实例都可以添加任意多个拦截器,他们组成一个队列,拦截器队列的执行顺序是FIFO。通过拦截器你可以在请求之前或响应之后(但还没有被 then 或 catchError处理)做一些统一的预处理操作。
dio.interceptors.add(InterceptorsWrapper(
onRequest:(RequestOptions options) async {
// 在请求被发送之前做一些事情
return options; //continue
// 如果你想完成请求并返回一些自定义数据,可以返回一个Response对象或返回dio.resolve(data)
// 这样请求将会被终止,上层then会被调用,then中返回的数据将是你的自定义数据data.
//
// 如果你想终止请求并触发一个错误,你可以返回一个DioError对象,或返回dio.reject(errMsg)
// 这样请求将被中止并触发异常,上层catchError会被调用。
},
onResponse:(Response response) async {
// 在返回响应数据之前做一些预处理
return response; // continue
},
onError: (DioError e) async {
// 当请求失败时做一些预处理
return e;//continue
}
));

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任务会进入一个队列,否则继续执行

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

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

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

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

尾声

评论里面有些同学有疑问关于如何学习material design控件,我的建议是去GitHub搜,有很多同行给的例子,这些栗子足够入门。

有朋友说要是动真格的话,需要NDK以及JVM等的知识,首现**NDK并不是神秘的东西,**你跟着官方的步骤走一遍就知道什么回事了,无非就是一些代码格式以及原生/JAVA内存交互,进阶一点的有原生/JAVA线程交互,线程交互确实有点蛋疼,但平常避免用就好了,再说对于初学者来说关心NDK干嘛,据鄙人以前的经历,只在音视频通信和一个嵌入式信号处理(离线)的两个项目中用过,嵌入式信号处理是JAVA->NDK->.SO->MATLAB这样调用的我原来MATLAB的代码,其他的大多就用在游戏上了吧,一般的互联网公司会有人给你公司的SO包的。
至于JVM,该掌握的那部分,相信我,你会掌握的,不该你掌握的,有那些专门研究JVM的人来做,不如省省心有空看看计算机系统,编译原理。

一句话,平常多写多练,这是最基本的程序员的素质,尽量挤时间,读理论基础书籍,JVM不是未来30年唯一的虚拟机,JAVA也不一定再风靡未来30年工业界,其他的系统和语言也会雨后春笋冒出来,但你理论扎实会让你很快理解学会一个语言或者框架,你平常写的多会让你很快熟练的将新学的东西应用到实际中。
初学者,一句话,多练。

嵌入式信号处理(离线)的两个项目中用过,嵌入式信号处理是JAVA->NDK->.SO->MATLAB这样调用的我原来MATLAB的代码,其他的大多就用在游戏上了吧,一般的互联网公司会有人给你公司的SO包的。**
至于JVM,该掌握的那部分,相信我,你会掌握的,不该你掌握的,有那些专门研究JVM的人来做,不如省省心有空看看计算机系统,编译原理。

一句话,平常多写多练,这是最基本的程序员的素质,尽量挤时间,读理论基础书籍,JVM不是未来30年唯一的虚拟机,JAVA也不一定再风靡未来30年工业界,其他的系统和语言也会雨后春笋冒出来,但你理论扎实会让你很快理解学会一个语言或者框架,你平常写的多会让你很快熟练的将新学的东西应用到实际中。
初学者,一句话,多练。

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

  • 23
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值