说一下我个人的想法啊,很多人觉得看源码特别难,无从下手,很多源码看不懂。我觉得这是很正常的事,除非某个框架代码是你写的,不然,你很难把每一句代码都搞懂,就连框架的作者,时间一久,都没有办法保证说每一句代码都能够看得懂。我们花个三四天,一两个星期,都很难把我们自己公司新接手的项目熟悉完,更何况是这些框架了。我说一下我平时是怎么看框架源码的吧,每一个框架,首先,会用,这是最基本的,连用都不会用,还别谈其他的了。其次,每一个框架,基本上都会有流程图,这些流程图,在网上都有。我们找到这个流程图,这个流程图,基本上都是整个框架的主干,我们顺着这个主干阅读,你就会发现,很明了,很清晰。当你把主体熟悉完以后,如果,你想更加深入的了解,你就可以在这个主体基础上慢慢的延伸出去。好了,废话不多说了,今天我们来了解一下OkHttp,这里是基于OkHttp3的源码,OkHttp2.x的源码和OkHttp3的会有点区别。我们按照上面的说的方式来阅读一下。
OkHttp流程图
OkHttp基本使用
gradle依赖
1 implementation 'com.squareup.okhttp3:okhttp:3.11.0'
2 implementation 'com.squareup.okio:okio:1.15.0'
1 /**
2 *这里拿get请求来
3 * 异步的get请求
4 */
5 public void okhttpAsyn() {
6 //设置超时的时间
7 OkHttpClient.Builder builder = new OkHttpClient.Builder()
8 .connectTimeout(15, TimeUnit.SECONDS)
9 .writeTimeout(20, TimeUnit.SECONDS)
10 .readTimeout(20, TimeUnit.SECONDS)
11 ;
12 OkHttpClient okHttpClient = builder.build();
13 Request request = new Request.Builder()
14 .get() //设置请求模式
15 .url("https://www.baidu.com/")
16 .build();
17
18 Call call = okHttpClient.newCall(request);
19 call.enqueue(new Callback() {
20 @Override
21 public void onFailure(Call call, IOException e) {
22 Log.d("MainActivity", "-----------onFailure-----------");
23 }
24
25 @Override
26 public void onResponse(Call call, Response response) throws IOException {
27 Log.d("MainActivity", "----onResponse----" + response.body().toString());
28 runOnUiThread(new Runnable() {
29 @Override
30 public void run() {
31 Toast.makeText(MainActivity.this, "请求成功", Toast.LENGTH_LONG).show();
32 }
33 });
34
35 }
36 });
37 }
OkHttp源码分析
从OkHttp的基本使用中,我们看到,通过okHttpClient.newCall()方法,拿到这个call对象,我们看看newCall是怎么走的
1 /**
2 * Prepares the {@code request} to be executed at some point in the future.
3 */
4 @Override public Call newCall(Request request) {
5 return RealCall.newRealCall(this, request, false /* for web socket */);
6 }
7
8
9 static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
10 // Safely publish the Call instance to the EventListener.
11 RealCall call = new RealCall(client, originalRequest, forWebSocket);
12 call.eventListener = client.eventListenerFactory().create(call);
13 return call;
14 }
从这里的源码知道,okHttpClient.newCall()实际上返回的是RealCall对象,而call.enqueue(),实际上是调用的了RealCall中的enqueue()方法,我们看看enqueue()方法方法怎么走。
1 @Override public void enqueue(Callback responseCallback) {
2 synchronized (this) {
3 if (executed) throw new IllegalStateException("Already Executed");
4 executed = true;
5 }
6 captureCallStackTrace();
7 eventListener.callStart(this);
8 client.dispatcher().enqueue(new AsyncCall(responseCallback));
9 }
可以看到client.dispatcher().enqueue(new AsyncCall(responseCallback));这句代码,也就是说,最终是有的请求是有dispatcher来完成,我们看看dispatcher。
1 /*
2 * Copyright (C) 2013 Square, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package okhttp3;
17
18 import java.util.ArrayDeque;
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.Deque;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.concurrent.ExecutorService;
25 import java.util.concurrent.SynchronousQueue;
26 import java.util.concurrent.ThreadPoolExecutor;
27 import java.util.concurrent.TimeUnit;
28 import javax.annotation.Nullable;
29 import okhttp3.RealCall.AsyncCall;
30 import okhttp3.internal.Util;
31
32 /**
33 * Policy on when async requests are executed.
34 *
35 * <p>Each dispatcher uses an {@link ExecutorService} to run calls internally. If you supply your
36 * own executor, it should be able to run {@linkplain #getMaxRequests the configured maximum} number
37 * of calls concurrently.
38 */
39 public final class Dispatcher {
40 //最大请求的并发数
41 private int maxRequests = 64;
42 //每个主机最大请求数
43 private int maxRequestsPerHost = 5;
44 private @Nullable Runnable idleCallback;
45
46 /** 消费线程池 */
47 private @Nullable ExecutorService executorService;
48
49 /** 准备运行的异步请求队列 */
50 private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
51
52 /** 正在运行的异步请求队列 */
53 private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
54
55 /** 正在运行的同步请求队列 */
56 private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
57
58 /** 构造方法 */
59 public Dispatcher(ExecutorService executorService) {
60 this.executorService = executorService;
61 }
62
63 public Dispatcher() {
64 }
65
66 public synchronized ExecutorService executorService() {
67 if (executorService == null) {
68 executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
69 new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
70 }
71 return executorService;
72 }
73
74
75
76
77 /**
78 *
79 *设置并发执行最大的请求数量
80 * <p>If more than {@code maxRequests} requests are in flight when this is invoked, those requests
81 * will remain in flight.
82 */
83 public synchronized void setMaxRequests(int maxRequests) {
84 if (maxRequests < 1) {
85 throw new IllegalArgumentException("max < 1: " + maxRequests);
86 }
87 this.maxRequests = maxRequests;
88 promoteCalls();
89 }
90
91 //获取到最大请求的数量
92 public synchronized int getMaxRequests() {
93 return maxRequests;
94 }
95
96 /**
97 * 设置每个主机并发执行的请求的最大数量
98 * <p>If more than {@code maxRequestsPerHost} requests are in flight when this is invoked, those
99 * requests will remain in flight.
100 *
101 * <p>WebSocket connections to hosts <b>do not</b> count against this limit.
102 */
103 public synchronized void setMaxRequestsPerHost(int maxRequestsPerHost) {
104 if (maxRequestsPerHost < 1) {
105 throw new IllegalArgumentException("max < 1: " + maxRequestsPerHost);
106 }
107 this.maxRequestsPerHost = maxRequestsPerHost;
108 promoteCalls();
109 }
110
111 //获取每个主机最大并发数量
112 public synchronized int getMaxRequestsPerHost() {
113 return maxRequestsPerHost;
114 }
115
116 /**
117 * Set a callback to be invoked each time the dispatcher becomes idle (when the number of running
118 * calls returns to zero).
119 *
120 * <p>Note: The time at which a {@linkplain Call call} is considered idle is different depending
121 * on whether it was run {@linkplain Call#enqueue(Callback) asynchronously} or
122 * {@linkplain Call#execute() synchronously}. Asynchronous calls become idle after the
123 * {@link Callback#onResponse onResponse} or {@link Callback#onFailure onFailure} callback has
124 * returned. Synchronous calls become idle once {@link Call#execute() execute()} returns. This
125 * means that if you are doing synchronous calls the network layer will not truly be idle until
126 * every returned {@link Response} has been closed.
127 */
128 public synchronized void setIdleCallback(@Nullable Runnable idleCallback) {
129 this.idleCallback = idleCallback;
130 }
131
132 synchronized void enqueue(AsyncCall call) {
133 if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
134 runningAsyncCalls.add(call);
135 executorService().execute(call);
136 } else {
137 readyAsyncCalls.add(call);
138 }
139 }
140
141 /**
142 * Cancel all calls currently enqueued or executing. Includes calls executed both {@linkplain
143 * Call#execute() synchronously} and {@linkplain Call#enqueue asynchronously}.
144 */
145 public synchronized void cancelAll() {
146 for (AsyncCall call : readyAsyncCalls) {
147 call.get().cancel();
148 }
149
150 for (AsyncCall call : runningAsyncCalls) {
151 call.get().cancel();
152 }
153
154 for (RealCall call : runningSyncCalls) {
155 call.cancel();
156 }
157 }
158
159 private void promoteCalls() {
160 if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
161 if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
162
163 for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
164 AsyncCall call = i.next();
165
166 if (runningCallsForHost(call) < maxRequestsPerHost) {
167 i.remove();
168 runningAsyncCalls.add(call);
169 executorService().execute(call);
170 }
171
172 if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
173 }
174 }
175
176 //----------------省略若干代码-----------------------
177
178 }
我们来找到这段代码
1 synchronized void enqueue(AsyncCall call) {
2 if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
3 runningAsyncCalls.add(call);
4 executorService().execute(call);
5 } else {
6 readyAsyncCalls.add(call);
7 }
8 }
当正在运行的异步请求队列中的数量小于64并且正在运行的请求主机数小于5时则把请求加载到runningAsyncCalls中并在线程池中执行,否则就再入到readyAsyncCalls中进行缓存等待。而runningAsyncCalls这个请求队列存放的就是AsyncCall对象,而这个AsyncCall就是RealCall的内部类,也就是说executorService().execute(call);实际上走的是RealCall类中的execute()方法.
1 @Override protected void execute() {
2 boolean signalledCallback = false;
3 try {
4 Response response = getResponseWithInterceptorChain();
5 if (retryAndFollowUpInterceptor.isCanceled()) {
6 signalledCallback = true;
7 responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
8 } else {
9 signalledCallback = true;
10 responseCallback.onResponse(RealCall.this, response);
11 }
12 } catch (IOException e) {
13 if (signalledCallback) {
14 // Do not signal the callback twice!
15 Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
16 } else {
17 eventListener.callFailed(RealCall.this, e);
18 responseCallback.onFailure(RealCall.this, e);
19 }
20 } finally {
21 client.dispatcher().finished(this);
22 }
23 }
这部分的代码,相信很多人都能够看的明白,无非就是一些成功,失败的回调,这段代码,最重要的是esponse response = getResponseWithInterceptorChain();和client.dispatcher().finished(this);我们先来看看client.dispatcher().finished(this);这句代码是怎么执行的。
1 /** Used by {@code AsyncCall#run} to signal completion. */
2 void finished(AsyncCall call) {
3 finished(runningAsyncCalls, call, true);
4 }
5
6 /** Used by {@code Call#execute} to signal completion. */
7 void finished(RealCall call) {
8 finished(runningSyncCalls, call, false);
9 }
10
11 private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
12 int runningCallsCount;
13 Runnable idleCallback;
14 synchronized (this) {
15 if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
16 if (promoteCalls) promoteCalls();
17 runningCallsCount = runningCallsCount();
18 idleCallback = this.idleCallback;
19 }
20
21 if (runningCallsCount == 0 && idleCallback != null) {
22 idleCallback.run();
23 }
24 }
25
26 private void promoteCalls() {
27 if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
28 if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
29
30 for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
31 AsyncCall call = i.next();
32
33 if (runningCallsForHost(call) < maxRequestsPerHost) {
34 i.remove();
35 runningAsyncCalls.add(call);
36 executorService().execute(call);
37 }
38
39 if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
40 }
41 }
由于client.dispatcher().finished(this);这句代码是放到finally中执行的,所以无论什么情况,都会执行上面的promoteCalls()方法,而从promoteCalls()方法中可以看出通过遍历来获取到下一个请求从而执行下一个网络请求。
回过头来,我们看看这一句代码Response response = getResponseWithInterceptorChain(); 通过getResponseWithInterceptorChain();来获取到response,然后回调返回。很明显getResponseWithInterceptorChain()这句代码里面进行了网络请求。我们看看是怎么执行的。
1 Response getResponseWithInterceptorChain() throws IOException {
2 // Build a full stack of interceptors.
3 List<Interceptor> interceptors = new ArrayList<>();
4 interceptors.addAll(client.interceptors());
5 interceptors.add(retryAndFollowUpInterceptor);
6 interceptors.add(new BridgeInterceptor(client.cookieJar()));
7 interceptors.add(new CacheInterceptor(client.internalCache()));
8 interceptors.add(new ConnectInterceptor(client));
9 if (!forWebSocket) {
10 interceptors.addAll(client.networkInterceptors());
11 }
12 interceptors.add(new CallServerInterceptor(forWebSocket));
13
14 Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
15 originalRequest, this, eventListener, client.connectTimeoutMillis(),
16 client.readTimeoutMillis(), client.writeTimeoutMillis());
17
18 return chain.proceed(originalRequest);
19 }
20 }
从上面代码可以知道,缓存,网络请求,都封装成拦截器的形式。拦截器主要用来观察,修改以及可能短路的请求输出和响应的回来。最后return chain.proceed,而chain是通过new RealInterceptorChain来获取到的,我们来看看RealInterceptorChain对象,然后找到proceed()方法。
1 public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
2 RealConnection connection) throws IOException {
3 if (index >= interceptors.size()) throw new AssertionError();
4
5 calls++;
6
7 // If we already have a stream, confirm that the incoming request will use it.
8 if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
9 throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
10 + " must retain the same host and port");
11 }
12
13 // If we already have a stream, confirm that this is the only call to chain.proceed().
14 if (this.httpCodec != null && calls > 1) {
15 throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
16 + " must call proceed() exactly once");
17 }
18
19 // 调用下一个拦截器
20 RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
21 connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
22 writeTimeout);
23 Interceptor interceptor = interceptors.get(index);
24 Response response = interceptor.intercept(next); //调用拦截器中的intercept()方法
25
26 // Confirm that the next interceptor made its required call to chain.proceed().
27 if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
28 throw new IllegalStateException("network interceptor " + interceptor
29 + " must call proceed() exactly once");
30 }
31
32 // Confirm that the intercepted response isn't null.
33 if (response == null) {
34 throw new NullPointerException("interceptor " + interceptor + " returned null");
35 }
36
37 if (response.body() == null) {
38 throw new IllegalStateException(
39 "interceptor " + interceptor + " returned a response with no body");
40 }
41
42 return response;
43 }
从上面的代码可以看出来,chain.proceed主要是讲集合中的拦截器遍历出来,然后通过调用每一个拦截器中的intercept()方法,然后获取到response结果,返回。
我们看看CacheInterceptor这个类,找到intercept()方法。
1 @Override public Response intercept(Chain chain) throws IOException {
2 Response cacheCandidate = cache != null
3 ? cache.get(chain.request())
4 : null;
5
6 long now = System.currentTimeMillis();
7
8 //创建CacheStrategy.Factory对象,进行缓存配置
9 CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
10 //网络请求
11 Request networkRequest = strategy.networkRequest;
12 //缓存响应
13 Response cacheResponse = strategy.cacheResponse;
14
15 if (cache != null) {
16 //记录当前请求是网络发起还是缓存发起
17 cache.trackResponse(strategy);
18 }
19
20 if (cacheCandidate != null && cacheResponse == null) {
21 closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
22 }
23
24 // 不进行网络请求并且缓存不存在或者过期则返回504错误
25 if (networkRequest == null && cacheResponse == null) {
26 return new Response.Builder()
27 .request(chain.request())
28 .protocol(Protocol.HTTP_1_1)
29 .code(504)
30 .message("Unsatisfiable Request (only-if-cached)")
31 .body(Util.EMPTY_RESPONSE)
32 .sentRequestAtMillis(-1L)
33 .receivedResponseAtMillis(System.currentTimeMillis())
34 .build();
35 }
36
37 // 不进行网络请求,而且缓存可以使用,直接返回缓存
38 if (networkRequest == null) {
39 return cacheResponse.newBuilder()
40 .cacheResponse(stripBody(cacheResponse))
41 .build();
42 }
43
44 //进行网络请求
45 Response networkResponse = null;
46 try {
47 networkResponse = chain.proceed(networkRequest);
48 } finally {
49 // If we're crashing on I/O or otherwise, don't leak the cache body.
50 if (networkResponse == null && cacheCandidate != null) {
51 closeQuietly(cacheCandidate.body());
52 }
53 }
54
55 //---------省略若干代码-------------
56
57 return response;
58 }
上面我做了很多注释,基本的流程是有缓存就取缓存里面的,没有缓存就请求网络。我们来看看网络请求的类CallServerInterceptor
1 /*
2 * Copyright (C) 2016 Square, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package okhttp3.internal.http;
17
18 import java.io.IOException;
19 import java.net.ProtocolException;
20 import okhttp3.Interceptor;
21 import okhttp3.Request;
22 import okhttp3.Response;
23 import okhttp3.internal.Util;
24 import okhttp3.internal.connection.RealConnection;
25 import okhttp3.internal.connection.StreamAllocation;
26 import okio.Buffer;
27 import okio.BufferedSink;
28 import okio.ForwardingSink;
29 import okio.Okio;
30 import okio.Sink;
31
32 /** This is the last interceptor in the chain. It makes a network call to the server. */
33 public final class CallServerInterceptor implements Interceptor {
34 private final boolean forWebSocket;
35
36 public CallServerInterceptor(boolean forWebSocket) {
37 this.forWebSocket = forWebSocket;
38 }
39
40 @Override public Response intercept(Chain chain) throws IOException {
41 RealInterceptorChain realChain = (RealInterceptorChain) chain;
42 HttpCodec httpCodec = realChain.httpStream();
43 StreamAllocation streamAllocation = realChain.streamAllocation();
44 RealConnection connection = (RealConnection) realChain.connection();
45 Request request = realChain.request();
46
47 long sentRequestMillis = System.currentTimeMillis();
48
49 realChain.eventListener().requestHeadersStart(realChain.call());
50 httpCodec.writeRequestHeaders(request);
51 realChain.eventListener().requestHeadersEnd(realChain.call(), request);
52
53 Response.Builder responseBuilder = null;
54 if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
55 // If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
56 // Continue" response before transmitting the request body. If we don't get that, return
57 // what we did get (such as a 4xx response) without ever transmitting the request body.
58 if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
59 httpCodec.flushRequest();
60 realChain.eventListener().responseHeadersStart(realChain.call());
61 responseBuilder = httpCodec.readResponseHeaders(true);
62 }
63
64 if (responseBuilder == null) {
65 // Write the request body if the "Expect: 100-continue" expectation was met.
66 realChain.eventListener().requestBodyStart(realChain.call());
67 long contentLength = request.body().contentLength();
68 CountingSink requestBodyOut =
69 new CountingSink(httpCodec.createRequestBody(request, contentLength));
70 BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
71
72 request.body().writeTo(bufferedRequestBody);
73 bufferedRequestBody.close();
74 realChain.eventListener()
75 .requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
76 } else if (!connection.isMultiplexed()) {
77 // If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
78 // from being reused. Otherwise we're still obligated to transmit the request body to
79 // leave the connection in a consistent state.
80 streamAllocation.noNewStreams();
81 }
82 }
83
84 httpCodec.finishRequest();
85
86 if (responseBuilder == null) {
87 realChain.eventListener().responseHeadersStart(realChain.call());
88 responseBuilder = httpCodec.readResponseHeaders(false);
89 }
90
91 Response response = responseBuilder
92 .request(request)
93 .handshake(streamAllocation.connection().handshake())
94 .sentRequestAtMillis(sentRequestMillis)
95 .receivedResponseAtMillis(System.currentTimeMillis())
96 .build();
97
98 int code = response.code();
99 if (code == 100) {
100 // server sent a 100-continue even though we did not request one.
101 // try again to read the actual response
102 responseBuilder = httpCodec.readResponseHeaders(false);
103
104 response = responseBuilder
105 .request(request)
106 .handshake(streamAllocation.connection().handshake())
107 .sentRequestAtMillis(sentRequestMillis)
108 .receivedResponseAtMillis(System.currentTimeMillis())
109 .build();
110
111 code = response.code();
112 }
113
114 realChain.eventListener()
115 .responseHeadersEnd(realChain.call(), response);
116
117 if (forWebSocket && code == 101) {
118 // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
119 response = response.newBuilder()
120 .body(Util.EMPTY_RESPONSE)
121 .build();
122 } else {
123 response = response.newBuilder()
124 .body(httpCodec.openResponseBody(response))
125 .build();
126 }
127
128 if ("close".equalsIgnoreCase(response.request().header("Connection"))
129 || "close".equalsIgnoreCase(response.header("Connection"))) {
130 streamAllocation.noNewStreams();
131 }
132
133 if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
134 throw new ProtocolException(
135 "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
136 }
137
138 return response;
139 }
140 }
到这里,基本上okhttp的整个流程就出来了,当然,这里只是一个整体的大概流程,如果要抠的很细,那就不是一篇文章能够说明的了了........现在回过头来再看一眼流程图,是不是感觉特别明朗了。