网站建设关于公司怎么写免费收录网站提交
目录
- 一、Okhttp请求
- 二、Retrofit 请求
- retrofit是如何封装请求的
- 三、Retrofit的构建过程
- 四、Retrofit构建IxxxService对象的过程(Retrofit.create())
- 4.1 动态代理
- 4.2 ServiceMethod
- 4.3 okHttpCall
- 4.4 callAdapter
- 五、Retrofit网络请求操作
一、Okhttp请求
示例代码:
private void testOkHttp() {// step1OkHttpClient client = new OkHttpClient();// step2Request request = new Request.Builder().url("https://www.google.com.hk").build();// step3Call call = client.newCall(request);// step4 发送网络请求,获取数据,进行后续处理call.enqueue(new okhttp3.Callback() {@Overridepublic void onFailure(okhttp3.Call call, IOException e) {}@Overridepublic void onResponse(okhttp3.Call call, okhttp3.Response response) throws IOException {}});}
上面示例代码解析:
step1:创建 HttpClient 对象,也就是构建一个网络类型的实例,一般会将所有的网络请求使用同一个单例对象。
step2:构建 Request,也就是构建一个具体的网络请求对象,具体的请求 url,请求头,请求体等等。
step3:构建请求 Call,也就是将具体的网络请求与执行请求的实体进行绑定,形成一个具体的正式的可执行体。
step4:后面就是进行网络请求了,然后处理网络请求的数据。
总结一下:
OkHttp 的意义:OkHttp 是基于 Http 协议封装的一套请求客户端,虽然它可以开线程,但根本上它更便向真正的请求,跟 HttpClient,HttpUrlConnection 的职责是一样的。
OkHttp 的职责:OkHttp 主要负责 socket 部分的优化,比如多路复用,buffer 缓存,数据压缩等等。
OkHttp 给用户留下的问题:
1)用户网络请求的接口配置繁琐,尤其是需要配置请求 body,请求头,参数的时候;
2)数据解析过程需要用户手动拿到 responsebody 进行解析,不能复用;
3)无法适配自动进行线程的切换;
4)万一存在网络嵌套网络请求就会陷入“回调陷阱”
二、Retrofit 请求
示例代码使用:
导入相关包:
// Retrofit的依赖
implementation 'com.squareup.retrofit2:retrofit:2.1.0'
implementation 'com.squareup.retrofit2:converter-gson:2.1.0'
代码示例
// step1 建立一个Retrofit对象,配置好Retrofit类的成员变量
Retrofit retrofit = new Retrofit.Builder().baseUrl("http://www.wanandroid.com") //网络请求的url地址.addConverterFactory(GsonConverterFactory.create(new Gson())).addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build();
// step2 创建一个ISharedListService接口类的对象,create函数内部使用了动态代理来创建接口
//对象,这样的设计可以让所有的访问请求都被代理
ISharedListService sharedListService = retrofit.create(ISharedListService.class);// step3 调用getSharedList的时候,在动态代理里面,会存在一个函数 getSharedLIst,
// 这个函数里面会调用 invoke,这个 invoke 函数也就是retrofit 里面的 invoke 函数
// 目的:将接口转换成网络请求
Call<SharedListBean> sharedListCall = sharedListService.getSharedList(2, 1); //这里返回的是ExecutorCallbackCall对象// step4
sharedListCall.enqueue(new Callback<SharedListBean>(){@Overridepublic void onResponse(Call<SharedListBean> call, Response<SharedListBean> response) {if (response.isSuccessful()) {System.out.println(response.body().toString());}}@Overridepublic void onFailure(Call<SharedListBean> call, Throwable t) {t.printStackTrace();}
})
上面示例代码解析:
step1: 创建 retrofit 对象,构建一个网络请求的载体对象,和 OkHttp 构建 OKHttpClient 对象有一样的意义,只不过 retrofit 在 build 的时候有非常多的初始化内容,这些内容可以为后面网络请求提供准备,如准备 线程转换Executor, Gson convert,RxjavaCallAdapter。
step2:Retrofit 的精髓,为统一配置网络请求完成动态代理的设置。
step3:构建具体网络请求对象 Request(service), 在这个阶段要完成的任务:
1)将接口中的注解翻译成对应的参数;
2)确定网络请求参数的返回值 response 类型以及对应的转换器;
3)将 OkHttp 的 Request 封装成为 Retrofit 的 OkhttpCall。
总的来说,就是根据请求 service 的 interface 来封装 Okhttp 请求 Request。
step4: 后面就进行网络请求,然后处理网络请求的数据。
其中:
baseUrl:网络请求的url地址
callFactory:网络请求工厂
callbackExecutor:回调方法执行器
adapterFactories:网络请求适配器工厂的集合
coverterFatories:数据转换器工厂的集合
总结一下:
Retrofit 主要负责应用层面的封装,就是说主要面向开发者,方便使用,比如请求参数,响应数据的处理,错误处理等等。
Retrofit 封装了具体的请求,线程切换以及数据转换。
网上一般都推荐 RxJava + Retrofit + OkHttp 框架,Retrofit 负责请求的数据和请求的结果,使用接口的方式呈现,OkHttp 负责请求的过程,Rxjava 负责异步,各种线程之间的切换,用起来非常便利。
ISharedListService.java
public interface ISharedListService {@GET("user/{userid}/share_articles/{pageid}/json")Call<SharedListBean> getSharedList(@Path("userid") int it, @Path("pageid") int pageid);}
retrofit是如何封装请求的
大体的网络流程是一致的,毕竟都是通过okhttp进行网络请求。主要的步骤都是:创建网络请求实体client -> 构建真正的网络请求 -> 将网络请求方案与真正的网络请求实体结合构成一个请求Call -> 执行网络请求 -> 处理返回数据 -> 处理 Android 平台的线程问题。
在上图中,我们看到的对比最大的区别是:
1)okhttp创建的是OkHttpClient,然而retrofit创建的是Retrofit实例
2)构建Request的流程,retrofit是通过注解来进行适配
3)配置Call的过程中,retrofit是利用Adapter适配的OkHttp的Call
4)相对okhttp,retrofit会对responseBody进行自动的Gson解析
5)相对okhttp,retrofit会自动的完成线程的切换
三、Retrofit的构建过程
Retrofit 通过build模式来生成一个Retrofit对象,通过代码我们知道,Retrofit默认会使用Okhttp来发送网络请求,当然,我们也可以自己定制。
执行step1中的build()操作,进入下面的build流程
Retrofit.java
public Retrofit build() {if (baseUrl == null) {throw new IllegalStateException("Base URL required.");}// 代码1// 默认只支持 okhttp 请求,不支持 httpurlconnection 和 httpclientokhttp3.Call.Factory callFactory = this.callFactory;if (callFactory == null) {callFactory = new OkHttpClient();}// 代码2// 添加一个线程管理 Executor, okhttp 切换线程需要手动处理,但是 retrofit//不需要,就是因为这个 Executor 的存在,其实它是handlerExecutor callbackExecutor = this.callbackExecutor;if (callbackExecutor == null) {callbackExecutor = platform.defaultCallbackExecutor();}// 代码3// Make a defensive copy of the adapters and add the default Call adapter.List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)); // 添加一个默认的callAdapterFatory// Make a defensive copy of the converters.List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,callbackExecutor, validateEagerly);}
其中,build流程采用构建/建造者设计模式和外观/门面设计模式。
1: 在代码1处
初始化构建call的工厂,但是这个地方直接就是使用了okhttp的call,没有使用到工厂设计模式去添加构建httpclient 或者 httpurlconnection的方法来创建call,说明retrofit已经铁下心只支持okhttp创建call请求。
此次调用,目的就是创建一个OkHttpClient,换句话说,这里的调用就是生产Okhttp网络请求需要的请求Call,以备后面进行真正的网络请求。
2:在代码2处
网络请求需要在子线程中执行,那么就需要线程管理,所有就有了代码2的存在,深入源码后发现,这个地方就是运用handler进行线程切换,当网络请求回来了进行线程切换,可以看下面的源码:
Platform.java
static class Android extends Platform {@Override public Executor defaultCallbackExecutor() {return new MainThreadExecutor();}@Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {if (callbackExecutor == null) throw new AssertionError();return new ExecutorCallAdapterFactory(callbackExecutor);}static class MainThreadExecutor implements Executor {private final Handler handler = new Handler(Looper.getMainLooper());@Override public void execute(Runnable r) {handler.post(r);}}}
所以,此次调用,目的是构建一个用handler封装的Executor,以备后面进行网络请求成功后的线程切换用。
3:在代码3处
设置默认CallAdapterFactory
在此添加的CallAdapterFactory属于系统默认的,当然,我们可以添加RxJavaCallAdapterFactory。默认的CallAdapterFactory是ExecutorCallAdapter类的对象,在Platform.java Class里面可以梳理出来
Platform.java
CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {if (callbackExecutor != null) {return new ExecutorCallAdapterFactory(callbackExecutor);}return DefaultCallAdapterFactory.INSTANCE;}
所以构建的Retrofit都是用于进行后面请求的需要的内容的一个准备工作。也就是封装OkHttp需要的准备工作。
最后new 了一个Retrofit对象
Retrofit.java
public final class Retrofit {// 网络请求配置对象(对网络请求接口中方法注解进行解析后得到的对象)// 作用:存储网络请求相关的配置,如网络请求的方法、数据转换器、网络请求适配器private final Map<Method, ServiceMethod<?, ?>> serviceMethodCache = new ConcurrentHashMap<>();// 网络请求器的工厂// 作用:生产网络请求器(Call)// Retrofit 是默认使用okhttpfinal okhttp3.Call.Factory callFactory;// 网络请求的url地址final HttpUrl baseUrl;// 数据转换器工厂的集合// 作用:放置数据转换器工厂// 数据转换器工厂作用:生产数据转换器(converter)final List<Converter.Factory> converterFactories;// 网络请求适配器工厂的集合// 作用:放置网络请求适配器工厂// 网络请求适配器工厂作用:生产网络请求适配器(CallAdapter)final List<CallAdapter.Factory> adapterFactories;// 回调方法执行器final @Nullable Executor callbackExecutor;// 标志位// 作用:是否提前对业务接口中的注解进行验证转换的标志位final boolean validateEagerly;Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories,@Nullable Executor callbackExecutor, boolean validateEagerly) {this.callFactory = callFactory;this.baseUrl = baseUrl;this.converterFactories = unmodifiableList(converterFactories); // Defensive copy at call site.this.adapterFactories = unmodifiableList(adapterFactories); // Defensive copy at call site.this.callbackExecutor = callbackExecutor;this.validateEagerly = validateEagerly;}...
}
四、Retrofit构建IxxxService对象的过程(Retrofit.create())
看下面的代码:
ISharedListService sharedListService = retrofit.create(ISharedListService.class);
Call<SharedListBean> sharedListCall = sharedListService.getSharedList(2, 1);
上面两行代码需要连起来才能正确的被阅读,因为,在create里面是使用了动态代理的技术方案,而动态代理是运行时生效的
执行step2,调用Retrofit的create()方法,create函数内部使用了动态代理来创建接口对象,这样的设计可以让所有的访问请求都被代理
create()方法里面采用动态代理设计模式
Retrofit.java
public <T> T create(final Class<T> service) {Utils.validateServiceInterface(service);if (validateEagerly) {eagerlyValidateMethods(service);}// 通过动态代理方式生成具体的网络请求实体对象return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },new InvocationHandler() { // 统一处理所有的请求方法private final Platform platform = Platform.get();@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)throws Throwable {// If the method is a method from Object then defer to normal invocation.// 说明方法的过滤,会过滤掉默认的不需要管理的方法if (method.getDeclaringClass() == Object.class) {return method.invoke(this, args);}// 默认情况下,java的接口是不能够有默认实现的,但是Java8 可以默认实现if (platform.isDefaultMethod(method)) {return platform.invokeDefaultMethod(method, service, proxy, args);}// 根据方法生成一个ServiceMethod对象(内部会将生成的ServiceMethod放入缓存中)// 如果已经生成过则直接从缓存中获取ServiceMethod<Object, Object> serviceMethod =(ServiceMethod<Object, Object>) loadServiceMethod(method);// 根据ServiceMethod对象和请求参数生成一个OkHttpCall对象,// 这个OkHttpCall能够调用OkHttp的接口发起网络请求OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);// 调用serviceMethod 的 callAdapter 的 adapter方法,并传入 okhttpCall,返回一个对象,// 这个的目的主要是为了适配返回类型,其内部会对OkHttpCall对象进行包装return serviceMethod.callAdapter.adapt(okHttpCall);}});}
1)Retrofit的create方法通过动态代理的模式,生成了实现具体的网络请求接口的对象,并在InvocationHandler的invoke方法中统一处理网络请求接口实体对象的方法;2)invoke方法会通过方法构造一个ServiceMethod对象,并将其放入缓存中;3)然后根据ServiceMethod对象和网络请求的参数args去构造一个OkhttpCall对象;4)最后调用serviceMethod的callAdapter的adapter方法,传入将OkHttpCall对象,callAdapter的目的主要是为了适配OkHttpCall对象,其内部会对OkHttpCall对象进行包装,生成对应返回类型的对象。
4.1 动态代理
动态代理的原理主要是在运行时动态生成代理类,然后根据代理类生成一个代理对象,在这个代理对象的方法中又会调用InvocationHandler的invoke来转发对方法的处理。
在使用retrofit的时候,对每一个网络请求的产生都必须要先调用create函数,也就意味着,我们的请求都是通过代理类来进行处理的。但是代理类具体的代理行为是发生在哪里呢?很明显,它并不是在create函数执行的时候,而是在使用具体的接口创建具体网络请求Call的时候,当调用具体网络请求Call的代码示例如下:
Call<SharedListBean> sharedListCall = sharedListService.getSharedList(2, 1);
在执行上面 代码的时候,它会走代码设计模式的InvocationHandler里面的invoke()函数,也就是所有的网络请求在创建具体网络请求Call的时候,都会走invoke, 从而我们可以在invoke里面进行各种行为的统一处理,比如:接口的统一处理,也就是注解的解读和网络请求参数的拼接。
在代码执行create()方法时,执行到Proxy.newProxInstance()时,在运行时会动态创建一个代理类对象类如ISharedListServiceProxy,并且该类实现了ISharedListService接口
代理类:
class ISharedListServiceProxy implement ISharedListService{// toString();// hashCode(); 等等一系列方法... // 接口方法public void getSharedList(int it, int pageid) {invocationHandler.invoke(it, pageid);}}
4.2 ServiceMethod
执行动态代理的invoke()方法,会执行 loadServiceMethod()方法
其实就将注解、参数都转化成网络请求Call的参数,一个请求对于一个ServiceMethod
Retrofit.java
ServiceMethod<?, ?> loadServiceMethod(Method method) {// 为什么会缓存--》为了效率ServiceMethod<?, ?> result = serviceMethodCache.get(method);if (result != null) return result;synchronized (serviceMethodCache) {result = serviceMethodCache.get(method);if (result == null) {result = new ServiceMethod.Builder<>(this, method).build(); // 采用一个建造者设计模式serviceMethodCache.put(method, result);}}return result;}
loadServiceMethod首先会从缓存中获取ServiceMethod对象,如果没有,则通过Method和Retrofit对象构造一个ServiceMethod对象,并将其放入缓存中。
每一个method都有一个自己的ServiceMethod,这就意味着ServiceMethod是属于函数的,而不是类的。也就是我们定义的网络访问接口类,在接口类里面的每一个函数都会在反射阶段形成自己的serviceMethod。
ServiceMethod其实是用来存储一次网络请求的基本信息的,比如Host、URL、请求方法等,同时ServiceMethod还会存储用来适配OkHttpCall对象的CallAdapter。ServiceMethod的build方法会解读传入的Method,首先ServiceMethod会在CallAdapterFactory列表中寻找合适的CallAdapter来包装OkHttpCall对象的,这一步主要是根据Method的返回参数来匹配的,比如如果方法的返回参数是Call对象,那么ServiceMethod就会使用默认的CallAdapterFactory来生成CallAdapter,而如果返回对象是RxJava的Observable对象,则会使用RxJavaCallAdapterFactory提供的CallAdapter。如果build方法会解读Method的注解,来获得注解上配置的网络请求信息,比如请求方法、URL、Header等。
ServiceMethod.java
final class ServiceMethod<R, T> {// Upper and lower characters, digits, underscores, and hyphens, starting with a character.static final String PARAM = "[a-zA-Z][a-zA-Z0-9_-]*";static final Pattern PARAM_URL_REGEX = Pattern.compile("\\{(" + PARAM + ")\\}");static final Pattern PARAM_NAME_REGEX = Pattern.compile(PARAM);// 网络请求工厂 也就是okhttp的网络请求 final okhttp3.Call.Factory callFactory;// 网络请求适配器 把call请求适配各个平台final CallAdapter<R, T> callAdapter;// 网络基地址private final HttpUrl baseUrl;// 数据转换器private final Converter<ResponseBody, R> responseConverter;// 网络请求的http方法 比如 get、post方法private final String httpMethod;// 网络请求的相对地址 和 baseUrl 拼接起来就是实际地址private final String relativeUrl;// 请求头private final Headers headers;// 网络请求的 http 报文的 typeprivate final MediaType contentType;// 三个标志位private final boolean hasBody;private final boolean isFormEncoded;private final boolean isMultipart;// 方法参数的处理器 比如我们定义的方法 上面的 get 和后面的参数private final ParameterHandler<?>[] parameterHandlers;// 构建一系列的变量ServiceMethod(Builder<R, T> builder) {this.callFactory = builder.retrofit.callFactory();this.callAdapter = builder.callAdapter;this.baseUrl = builder.retrofit.baseUrl();this.responseConverter = builder.responseConverter;this.httpMethod = builder.httpMethod;this.relativeUrl = builder.relativeUrl;this.headers = builder.headers;this.contentType = builder.contentType;this.hasBody = builder.hasBody;this.isFormEncoded = builder.isFormEncoded;this.isMultipart = builder.isMultipart;this.parameterHandlers = builder.parameterHandlers;}static final class Builder<T, R> {final Retrofit retrofit;final Method method;final Annotation[] methodAnnotations;final Annotation[][] parameterAnnotationsArray;final Type[] parameterTypes;Type responseType;boolean gotField;boolean gotPart;boolean gotBody;boolean gotPath;boolean gotQuery;boolean gotUrl;String httpMethod;boolean hasBody;boolean isFormEncoded;boolean isMultipart;String relativeUrl;Headers headers;MediaType contentType;Set<String> relativeUrlParamNames;ParameterHandler<?>[] parameterHandlers;Converter<ResponseBody, T> responseConverter;CallAdapter<T, R> callAdapter;Builder(Retrofit retrofit, Method method) {this.retrofit = retrofit;this.method = method;this.methodAnnotations = method.getAnnotations();this.parameterTypes = method.getGenericParameterTypes();this.parameterAnnotationsArray = method.getParameterAnnotations();}public ServiceMethod build() {callAdapter = createCallAdapter(); //查找能适配返回类型的CallAdapterresponseType = callAdapter.responseType();if (responseType == Response.class || responseType == okhttp3.Response.class) {throw methodError("'"+ Utils.getRawType(responseType).getName()+ "' is not a valid response body type. Did you mean ResponseBody?");}// 设置请求的数据适配器converterresponseConverter = createResponseConverter();// 解读方法的注解,methodAnnotations表示方法上的所有注解 for (Annotation annotation : methodAnnotations) {parseMethodAnnotation(annotation);}if (httpMethod == null) {throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");}if (!hasBody) {if (isMultipart) {throw methodError("Multipart can only be specified on HTTP methods with request body (e.g., @POST).");}if (isFormEncoded) {throw methodError("FormUrlEncoded can only be specified on HTTP methods with "+ "request body (e.g., @POST).");}}int parameterCount = parameterAnnotationsArray.length;parameterHandlers = new ParameterHandler<?>[parameterCount];for (int p = 0; p < parameterCount; p++) {Type parameterType = parameterTypes[p];if (Utils.hasUnresolvableType(parameterType)) {throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",parameterType);}Annotation[] parameterAnnotations = parameterAnnotationsArray[p];if (parameterAnnotations == null) {throw parameterError(p, "No Retrofit annotation found.");}parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);}if (relativeUrl == null && !gotUrl) {throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);}if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {throw methodError("Non-body HTTP method cannot contain @Body.");}if (isFormEncoded && !gotField) {throw methodError("Form-encoded method must contain at least one @Field.");}if (isMultipart && !gotPart) {throw methodError("Multipart method must contain at least one @Part.");}return new ServiceMethod<>(this); // new了一个ServiceMethod}
ServiceMethod 作用就是解析注解、解析请求的返回值这一系列过程
retrofit是对整个网络请求的一个保存,ServiceMethod是对某一个接口请求(具体的网络请求)的保存,就是从retrofit中所保存的一系列参数列表中找到对应的参数
4.3 okHttpCall
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
我们知道,ServiceMethod封装了网络请求的基本信息,比如Host、URL等,我们根据ServiceMethod和请求参数args就可以确定本次网络请求的所有信息了,OkHttpCall主要是将这些信息封装起来,并调用OkHttp的接口去发送网络请求,这里,我们就将OkHttpCall看成是一个处理网络请求的类即可。
4.4 callAdapter
在retrofit中,invoke()里面的最后一行代码,
return serviceMethod.callAdapter.adapt(okHttpCall);
那么我们可以设想一下为什么Retrofit还要设计一个CallAdapter接口呢?
先来说一个客观事实,Retrofit真正使用okhttp进行网络请求的就是OkHttpCall这个类
曾提到了Call对象的创建是通过ServiceMethod.adapter()完成的,这里在看看该方法的源码:
ServiceMethod.adapter()方法:
T adapte(Call<R> call) {return callAdapter.adapte(call);
}
通过上述源码可知,最终Call对象是调用CallAdapter.adapter(Call)方法创建的,那么CallAdapter及具体的Call对象又是如何生成的呢?
如果没有这个适配器模式,会出现上面情况?
很明显,没有适配器的时候此时我们网络请求的返回接口只能直接返回OkHttpCall,那么所有的网络请求都是用okhttpCall进行,这样的话就失去了retrofit封装网络请求call的意义了,譬如:RxjavaCallAdapterFactory就没有办法支持。
如果我们想要返回的不是Call呢?比如Rxjava的Observable,这种情况下该怎么办呢?
适配器模式在此发挥了其应用的作用
将网络请求的核心类OkHttpCall进行适配,你需要什么类型的数据就通过适配器适配,返回适配后的对象就是了,正是这种CallAdapter接口的设计,使得我们在使用Retrofit的时候可以自定义我们想要的返回类型。此接口的设计也为Rxjava的扩展使用做了很好的基础。
五、Retrofit网络请求操作
一般的Retrofit网络请求的操作是指Call.execute() & Call.enqueue()的过程,这个过程才是真正的网络请求,因为,网络配置,请求地址配置,Call适配,网络请求requestBody & 返回值responseBody转化适配准备工作都已经完成。
sharedListCall.enqueue(new Callback()...);
sharedListCall.execute();
在进行网络请求执行的时候,基本上就是调用,ServiceMethod中设置的各个内容如:
1)OkHttpCall进行网络请求,实则是进行okhttp的网络请求;
2)利用converter进行网络请求数据的转换,一般是Gson();
3)利用rxjava observable构建rxjava类型的责任链访问方案,并进行线程切换;
4)如果没有rxjava的添加,那么就使用默认的callAdapter里面的callbackExecutor进行线程的切换,进行网络请求。
整体网络请求的流程图: