安卓总结 之 OkHttp使用及源码分析(三)

本文详细介绍了OkHttp在Android中的使用,包括依赖配置、网络请求的基本操作,如GET、POST异步请求以及文件上传下载。接着深入探讨了OkHttp的源码,分析了OkHttpClient.Builder的初始化、请求的取消机制、调度器Dispatcher的工作原理,以及拦截器链的执行过程。最后,简要阐述了网络请求的流程和缓存策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本章主要介绍Okhttp的使用和和源码分析

  1. 准备工作
  2. 常见用法
  3. OkHttp更好的封装
  4. OkHttp的源码分析

一. 准备工作

在gradle中添加依赖

    implementation 'com.squareup.okio:okio:1.7.0'
    implementation 'com.squareup.okhttp3:okhttp:3.2.0'

添加网络权限

   <uses-permission android:name="android.permission.INTERNET"/>

二. 常见用法

  1. get异步请求
private void getAsynHttp() {

        Request.Builder requestBuilder = new Request.Builder().url("http://www.baidu.com");
        requestBuilder.method("GET", null);
        Request request = requestBuilder.build();
        Call mcall = mOkHttpClient.newCall(request);
        mcall.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Toast.makeText(getApplicationContext(), "请求失败", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String str = response.body().string();
                Log.i(TAG, str);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(getApplicationContext(), "请求成功", Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });
    }
  1. post异步请求
    private void postAsynHttp() {
        RequestBody formBody = new FormBody.Builder()
                .add("ip", "59.108.54.37")
                .build();
        Request request = new Request.Builder()
                .url("http://ip.taobao.com/service/getIpInfo.php")
                .post(formBody)
                .build();
        OkHttpClient mOkHttpClient = new OkHttpClient();
        Call call = mOkHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Toast.makeText(getApplicationContext(), "请求失败", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String str = response.body().string();
                Log.d(TAG, str);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(getApplicationContext(), "请求成功", Toast.LENGTH_SHORT).show();
                    }
                });
            }

        });
    }

只是添加了一个FormBody

  1. 异步上传文件
    private void postAsynFile() {
        String filepath = "";
        if (Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED)) {
            filepath = Environment.getExternalStorageDirectory().getAbsolutePath();
        } else {
            filepath = getFilesDir().getAbsolutePath();
        }
        File file = new File(filepath, "wangshu.txt");
        Request request = new Request.Builder()
                .url("https://api.github.com/markdown/raw")
                .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
                .build();
        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.d(TAG, response.body().string());
            }
        });
    }

定义文件类型再调用RequestBody方法就ok

  1. 异步下载文件
    private void downAsynFile() {
        String url = "https://img-my.youkuaiyun.com/uploads/201603/26/1458988468_5804.jpg";
        Request request = new Request.Builder().url(url).build();
        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Toast.makeText(getApplicationContext(), "文件下载失败", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onResponse(Call call, Response response) {
                InputStream inputStream = response.body().byteStream();
                FileOutputStream fileOutputStream = null;
                String filepath = "";
                try {
                    if (Environment.getExternalStorageState().equals(
                            Environment.MEDIA_MOUNTED)) {
                        filepath = Environment.getExternalStorageDirectory().getAbsolutePath();
                    } else {
                        filepath = getFilesDir().getAbsolutePath();
                    }
                    File file = new File(filepath, "wangshu.jpg");
                    if (null != file) {
                        fileOutputStream = new FileOutputStream(file);
                        byte[] buffer = new byte[2048];
                        int len = 0;
                        while ((len = inputStream.read(buffer)) != -1) {
                            fileOutputStream.write(buffer, 0, len);
                        }
                        fileOutputStream.flush();
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                Toast.makeText(getApplicationContext(), "文件存储成功", Toast.LENGTH_SHORT).show();
                            }
                        });
                    } else {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                Toast.makeText(getApplicationContext(), "文件存储失败", Toast.LENGTH_SHORT).show();
                            }
                        });
                    }
                } catch (IOException e) {
                    Log.e(TAG, "IOException");
                    e.printStackTrace();
                }
            }
        });
    }

调用response.body().byteStream();得到输入流然后就是文件操作了

  1. 初始化okClient
    private void initOkHttpClient() {
        File sdcache = getExternalCacheDir();
        int cacheSize = 10 * 1024 * 1024;
        OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .connectTimeout(15, TimeUnit.SECONDS)
                .writeTimeout(20, TimeUnit.SECONDS)
                .readTimeout(20, TimeUnit.SECONDS)
                .cache(new Cache(sdcache.getAbsoluteFile(), cacheSize));
        mOkHttpClient = builder.build();
    }

在okHttp中,所有所有对于连接的初始化操作都在OkHttpClient.Builder进行,这里设置了超市时间和缓存。

  1. 取消请求

当用户离开应用程序或者跳转到其他页面的时候,我们可以取消任务节省网络资源。

简单的方法就是在Request.Builder.tag中分配一个标签,然后我们就能用OkHtppClient.cancel(Object tag)来取消任务

三. OkHttp更好的封装

public class OkHttpEngine {
    private static volatile  OkHttpEngine mInstance;
    private OkHttpClient mOkHttpClient;
    private Handler mHandler;

    // 双重检验锁创建OkHttpEngine单例
    public static OkHttpEngine getInstance(Context context) {
        if (mInstance == null) {
            synchronized (OkHttpEngine.class) {
                if (mInstance == null) {
                    mInstance = new OkHttpEngine(context);
                }
            }
        }
        return mInstance;
    }

    // 在构造函数中初始化Client和handler
    private OkHttpEngine(Context context) {
        File sdcache = context.getExternalCacheDir();
        int cacheSize = 10 * 1024 * 1024;
        OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .connectTimeout(15, TimeUnit.SECONDS)
                .writeTimeout(20, TimeUnit.SECONDS)
                .readTimeout(20, TimeUnit.SECONDS)
                .cache(new Cache(sdcache.getAbsoluteFile(), cacheSize));
         mOkHttpClient=builder.build();
         mHandler = new Handler();
    }


    /**
     * 异步get请求
     * @param url
     * @param callback
     */
    public void getAsynHttp(String url, ResultCallback callback) {

        final Request request = new Request.Builder()
                .url(url)
                .build();
        Call call = mOkHttpClient.newCall(request);
        dealResult(call, callback);
    }


    private void dealResult(Call call, final ResultCallback callback) {
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                sendFailedCallback(call.request(), e, callback);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                sendSuccessCallback(response.body().string(), callback);
            }

            private void sendSuccessCallback(final String str, final ResultCallback callback) {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        if (callback != null) {
                            try {
                                callback.onResponse(str);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                });
            }

            private void sendFailedCallback(final Request request, final Exception e, final ResultCallback callback) {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        if (callback != null)
                            callback.onError(request, e);
                    }
                });
            }

        });
    }
    

    public abstract class ResultCallback{
        public abstract void onError(Request request, Exception e);
        public abstract void onResponse(String str) throws IOException;
    }
}

请求网络的时候是用Handler将请求结果回调给UI线程,所以我们想要请求网络的时候只需要调用OkHttpEngine的getAsynHttp方法并写一个ResultCallback回调就可以了。

四. OkHttp的源码分析

我们从使用开始,一步步剖析OkClient的源码实现

//下面是一段Kotlin代码
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val request = Request.Builder().url("https://www.baidu.com/").method("get", null).build()
        OkHttpClient().newCall(request).enqueue(object : Callback {
            override fun onFailure(call: Call?, e: IOException?) {
                Log.d("onClient", "isBad")
            }

            override fun onResponse(call: Call?, response: Response?) {
                Log.e("onClient", "isOk")
            }
        })
    }

我们把重点关注在 OkHttpClient().newCall(request).enqueue() 这个方法中

newCall方法:

  // OkHttpClint中的newCall方法
  @Override public Call newCall(Request request) {
    return new RealCall(this, request);
  }

实例化一个RealCall类,RealCall实现了Call接口,看一下Call是干什么的
,这里保留了源码的英文注释

// A call is a request that has been prepared for execution. 
public interface Call {
    //  Invokes the request immediately, and blocks until the response can be processed or is in  error.
    Response execute() throws IOException;
    
    // Schedules the request to be executed at some point in the future.
    void enqueue(Callback responseCallback);
    
    void cancel();
    boolean isExecuted();
    boolean isCanceled();
    interface Factory {
    Call newCall(Request request);
  }
}

然后我们继续看OkHttpClient().newCall(request).enqueue()
的enqueue方法

  // RealCall中的enqueue方法
  void enqueue(Callback responseCallback, boolean forWebSocket) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));
  }

其中这个参数responseCallback就是源码介绍开头中OkHttpClient().newCall(request).enqueue()传入的callback

我们这里可以看到,真正处理这个Callback的并不是RealCall,而是dispatcher(),这个就是调度器,看看他的类声明 public final class Dispatcher 是一个不变类,也就是说不能被重写。

我们看一下调度器Dispatcher中的enqueue方法:

 // Dispatcher类的enqueue方法
  synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

这个方法用了synchronized声明。看到这么多全局的变量,感觉有点烦,没事,其实很简单,我们看一下Dispatcher的声明:

 // Dispatcher类的成员变量
  private int maxRequests = 64;
  private int maxRequestsPerHost = 5;

  /** Executes calls. Created lazily. */
  private ExecutorService executorService;

  /** Ready async calls in the order they'll be run. */
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

  /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

  /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

是不是感觉很清晰,很简单

我们回头看这段代码

 // Dispatcher类的enqueue方法
  synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

很简单,如果可以这个符合可以运行的条件,就把它加到runningAsyncCalls队列中,然后调用execute执行,否则加到readyAsyncCalls准备队列中

ok,我们把关注点放在这个方法中
executorService().execute(call);

// Dispatcher的executorService方法
public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }
 
 // 对比一下newCachedThreadPool,简直就是一毛一样好吧
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

我们知道CachedThreadPool实际上就是一个无限容量,但是队列中只有一个线程的线程池。这种线程池比较适合任务比较多,但是任务比较小的情况。

execute(Runnable run)调度方法最后会调用调用这个runnable的run方法,因为这个call是AsyncCall类,我们看看这个类的run方法:

final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;
    private final boolean forWebSocket;
    private AsyncCall(Callback responseCallback, boolean forWebSocket) {
      super("OkHttp %s", originalRequest.url().toString());
      this.responseCallback = responseCallback;
      this.forWebSocket = forWebSocket;
    }
    @Override protected void execute() {
      boolean signalledCallback = false;
      try {
        Response response = getResponseWithInterceptorChain(forWebSocket);
        if (canceled) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
          // Do not signal the callback twice!
          logger.log(Level.INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }

没有run方法呀,怎么肥事?看看它继承的类NamedRunnable搞了什么。

public abstract class NamedRunnable implements Runnable {
  protected final String name;

  public NamedRunnable(String format, Object... args) {
    this.name = String.format(format, args);
  }

  @Override public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }
  protected abstract void execute();
}

原来是它在run方法执行了execute方法,行吧,其实一样,线程池最后执行的是AsyncCall的execute方法。ojbk,来看一下吧!

   // AsyncCall的execute方法
    @Override protected void execute() {
      boolean signalledCallback = false;
      try {
        Response response = getResponseWithInterceptorChain(forWebSocket);
        if (canceled) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
          // Do not signal the callback twice!
          logger.log(Level.INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }

别急,我们一个个往下面看,先看这个方法:getResponseWithInterceptorChain

  private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException {
    Interceptor.Chain chain = new ApplicationInterceptorChain(0, originalRequest, forWebSocket);
    return chain.proceed(originalRequest);
  }

ApplicationInterceptorChain 从名字可以猜测到,他是一个拦截链,看一下吧!

 // ApplicationInterceptorChain类的proceed方法
 @Override public Response proceed(Request request) throws IOException {
      // If there's another interceptor in the chain, call that.
      if (index < client.interceptors().size()) {
        Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
        // 得到了当前拦截器
        Interceptor interceptor = client.interceptors().get(index);
        // 拦截操作,所有拦截器(除了最后一个)都会阻塞到这里
        Response interceptedResponse = interceptor.intercept(chain);

        if (interceptedResponse == null) {
          throw new NullPointerException("application interceptor " + interceptor
              + " returned null");
        }

        return interceptedResponse;
      }
      return getResponse(request, forWebSocket);
    }
  }

看到这段,有点发蒙

Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);

为什么还要创建一个chain呢,原来index代表当前拦截器所在的位置,比如一共有五个拦截器,拦截器“G”在第二个,那你要做的是将“G”拦截器拦截在第一个拦截器之后。get到了吗?

Response interceptedResponse = interceptor.intercept(chain);
这个方法为什么会拦住所有的拦截器,大家可能不明白,我就随便创建一个拦截器(拦截发出的请求和响应的日志)出来,大家就知道了。

class LoggingInterceptor implements Interceptor {
  @Override public Response intercept(Interceptor.Chain chain) throws IOException {
    Request request = chain.request();
    
    long t1 = System.nanoTime();
    logger.info(String.format("Sending request %s on %s%n%s",
        request.url(), chain.connection(), request.headers()));

    Response response = chain.proceed(request);

    long t2 = System.nanoTime();
    logger.info(String.format("Received response for %s in %.1fms%n%s",
        response.request().url(), (t2 - t1) / 1e6d, response.headers()));

    return response;
  }
}

大家能够明白吗,这里我画一张图:

拦截器.png

这个有点类似Spirng中的拦截器,其实是一个道理。

ok,我们讨论完了拦截器。回到AsyncCall的execute方法。

   // AsyncCall的execute方法
    @Override protected void execute() {
      boolean signalledCallback = false;
      try {
        Response response = getResponseWithInterceptorChain(forWebSocket);
        if (canceled) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
          // Do not signal the callback twice!
          logger.log(Level.INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  responseCallback.onFailure(RealCall.this, responseCallback.onResponse(RealCall.this, response);

这里正是我们重写的Callback的两个方法。

好了,做了这么久的前戏,也该开始网络请求了吧?看这个方法
client.dispatcher().finished(this); 是调度器的finish方法。

   // Dispatcher类的finished方法
   synchronized void finished(AsyncCall call) {
    if (!runningAsyncCalls.remove(call)) throw new AssertionError("AsyncCall wasn't running!");
    promoteCalls();
  }

这里简单地把call从运行队列中移走了。看一下PromoteCalls方法:

 // Dispatcher类中的promoteCalls方法
 private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
    if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

    for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
      AsyncCall call = i.next();

      if (runningCallsForHost(call) < maxRequestsPerHost) {
        i.remove();
        runningAsyncCalls.add(call);
        executorService().execute(call);
      }

      if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
    }
  }

很简单,running的一个call被消化了,这个队列也就有了空位置,这时候ready队列有call乘机上位。等等,说好的网络请求呢,在哪?是不是哪里疏忽了?

我们分析到AsyncCall的execute方法的时候,还记得它做了什么吗,它用一大堆拦截链拦截他,然后在最后 client.dispatcher().finished(this);对调度器Dispactcher的两个队列进行收尾对吧?

于是我们大概分析到网络请求操作在它们中间!果然在拦截器中间ApplicationInterceptorChain类的proceed方法找到啦!

 // ApplicationInterceptorChain类的proceed方法
 @Override public Response proceed(Request request) throws IOException {
      // If there's another interceptor in the chain, call that.
      if (index < client.interceptors().size()) {
        Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
        // 得到了当前拦截器
        Interceptor interceptor = client.interceptors().get(index);
        // 拦截操作,所有拦截器(除了最后一个)都会阻塞到这里
        Response interceptedResponse = interceptor.intercept(chain);

        if (interceptedResponse == null) {
          throw new NullPointerException("application interceptor " + interceptor
              + " returned null");
        }

        return interceptedResponse;
      }
      return getResponse(request, forWebSocket);
    }
  }

网络请求在这里:getResponse(request, forWebSocket);

这个方法又臭又长,这里我把重要的贴出来。

Response getResponse(Request request, boolean forWebSocket) throws IOException {
    engine.sendRequest();
    engine.readResponse();
}

喵喵喵!就这么短

一个个看啰!
HttpEngine的sendRequest方法也很复杂,主要解决的是缓存问题,我们就不展开了,不然要要要讲到明年!

// HttpEngine的sendRequest方法
 public void sendRequest() throws RequestException, RouteException, IOException {
    if (cacheStrategy != null) return; 
    if (httpStream != null) throw new IllegalStateException();

    Request request = networkRequest(userRequest);
    
    //获取Client中的Cache,同时Cache在初始化的时候读取缓存目录中曾经请求过的所有信息。
    InternalCache responseCache = Internal.instance.internalCache(client);
    Response cacheCandidate = responseCache != null
        ? responseCache.get(request)
        : null;

    long now = System.currentTimeMillis();
    cacheStrategy = new CacheStrategy.Factory(now, request, cacheCandidate).get();
    //网络请求
    networkRequest = cacheStrategy.networkRequest;
    //缓存的响应
    cacheResponse = cacheStrategy.cacheResponse;

    if (responseCache != null) {
        //记录当前请求是网络发起的还是缓存发起的
      responseCache.trackResponse(cacheStrategy);
    }

    
    if (cacheCandidate != null && cacheResponse == null) {
      closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
    }

    // 不进行网络请求并且缓存不存在或者过期,返回504错误
    if (networkRequest == null && cacheResponse == null) {
      userResponse = new Response.Builder()
          .request(userRequest)
          .priorResponse(stripBody(priorResponse))
          .protocol(Protocol.HTTP_1_1)
          .code(504)
          .message("Unsatisfiable Request (only-if-cached)")
          .body(EMPTY_BODY)
          .build();
      return;
    }
    
    // 不进行网络请求而且缓存可以使用,则直接返回缓存
    if (networkRequest == null) {
      userResponse = cacheResponse.newBuilder()
          .request(userRequest)
          .priorResponse(stripBody(priorResponse))
          .cacheResponse(stripBody(cacheResponse))
          .build();
      userResponse = unzip(userResponse);
      return;
    }

    // 需要访问网络时
    boolean success = false;
    try {
      httpStream = connect();
      httpStream.setHttpEngine(this);

cacheCandidate是上次与服务器交互时缓存的Response,这里的缓存均基于Map。key是请求中url的md5,value是在文件中查到的缓存,页面置换算法基于LRU。

通过cacheStrategy我们可以得到networkRequest和cacheResponse,代表网络请求和缓存是否存在。如果networkRequest和cacheResponse都为null的时候,返回504错误,当networkRequest为null时,也就是不进行网络请求时,就可以直接返回缓存,其他情况就请求网络。

我们粗略看一下readResponse()方法:

 public void readResponse() throws IOException {
    Response networkResponse;
    if (forWebSocket) {
      httpStream.writeRequestHeaders(networkRequest);
      // 读取网络响应
      networkResponse = readNetworkResponse();
    } else if (!callerWritesRequestBody) {
     // 检查缓存是否可用,如果可用则使用当前缓存的Response,关闭网络连接,释放连接
      networkResponse = new NetworkInterceptorChain(0, networkRequest).proceed(networkRequest);
    } else {
      // Emit the request body's buffer so that everything is in requestBodyOut.
      if (bufferedRequestBody != null && bufferedRequestBody.buffer().size() > 0) {
        bufferedRequestBody.emit();
      }

      // Emit the request headers if we haven't yet. We might have just learned the Content-Length.
      if (sentRequestMillis == -1) {
        if (OkHeaders.contentLength(networkRequest) == -1
            && requestBodyOut instanceof RetryableSink) {
          long contentLength = ((RetryableSink) requestBodyOut).contentLength();
          networkRequest = networkRequest.newBuilder()
              .header("Content-Length", Long.toString(contentLength))
              .build();
        }
        httpStream.writeRequestHeaders(networkRequest);
      }

      // Write the request body to the socket.
      if (requestBodyOut != null) {
        if (bufferedRequestBody != null) {
          // This also closes the wrapped requestBodyOut.
          bufferedRequestBody.close();
        } else {
          requestBodyOut.close();
        }
        if (requestBodyOut instanceof RetryableSink) {
          httpStream.writeRequestBody((RetryableSink) requestBodyOut);
        }
      }

      networkResponse = readNetworkResponse();
    }

    receiveHeaders(networkResponse.headers());

    // If we have a cache response too, then we're doing a conditional get.
    if (cacheResponse != null) {
      if (validate(cacheResponse, networkResponse)) {
        userResponse = cacheResponse.newBuilder()
            .request(userRequest)
            .priorResponse(stripBody(priorResponse))
            .headers(combine(cacheResponse.headers(), networkResponse.headers()))
            .cacheResponse(stripBody(cacheResponse))
            .networkResponse(stripBody(networkResponse))
            .build();
        networkResponse.body().close();
        releaseStreamAllocation();

        // Update the cache after combining headers but before stripping the
        // Content-Encoding header (as performed by initContentStream()).
        InternalCache responseCache = Internal.instance.internalCache(client);
        responseCache.trackConditionalCacheHit();
        responseCache.update(cacheResponse, stripBody(userResponse));
        userResponse = unzip(userResponse);
        return;
      } else {
        closeQuietly(cacheResponse.body());
      }
    }

    userResponse = networkResponse.newBuilder()
        .request(userRequest)
        .priorResponse(stripBody(priorResponse))
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build();

    if (hasBody(userResponse)) {
      maybeCache();
      userResponse = unzip(cacheWritingResponse(storeRequest, userResponse));
    }
  }

这个方法做的是解析HTTP响应报头。如果有缓存而且可用,则用缓存的数据并更新缓存,否则则用网络请求返回的数据。

总结完了,大家应该会觉得很乱,这里粗略画个图帮助大家理解。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值