1、源码地址:

  •   https://github.com/square/okhttp
  •  依赖:implementation 'com.squareup.okhttp3:okhttp:3.11.0'

2、原理图:

 

      

 

3、用法

     (1)创建OkHttpClient,单例:

        mOkHttpClient = new OkHttpClient.Builder()
                .writeTimeout(15000, TimeUnit.MILLISECONDS)
                .readTimeout(15000, TimeUnit.MILLISECONDS)
                .connectTimeout(15000, TimeUnit.MILLISECONDS)
                .retryOnConnectionFailure(true)
                .cache(new Cache(getCacheDir(), 10 * 1024 * 1024))
                .addInterceptor(new LoggingInterceptor())
                .build();

       (2)GET方法请求数据:

    private void getData() {
        Request request = new Request.Builder()
                .url(getUrlWithParams())
                .method("GET", null)
                .header("User-Agent", "My super agent")
                .addHeader("Accept", "text/html")
                .build();
        Call call = mOkHttpClient.newCall(request);
        try {
            //同步请求数据
            Response response = call.execute();
            if (!response.isSuccessful()) {
                Log.d("Andy", "get request fail.");
                return;
            }
            String result = response.body().string();
            Log.d("Andy", "result = " + result);
        } catch (IOException e) {

        }

        //异步请求数据
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
            }
        });

    }

    private String getUrlWithParams() {
        Uri.Builder uri = Uri.parse("https://www.baidu.com").buildUpon();
        uri.appendQueryParameter("imei", "jrasjgirojegorio;grje90e00")
                .appendQueryParameter("nt", "wifi");
        return uri.build().toString();
    }

        (3)  POST方法请求数据:

    private void postData() {
        FormBody body = new FormBody.Builder()
                .add("size", "100")
                .add("imei", "jgorejgprejgrepjgr")
                .build();
        Request request = new Request.Builder()
                .url("https://www.baidu.com")
                .post(body)
                .header("User-Agent", "My super agent")
                .addHeader("Accept", "text/html")
                .build();
        ...............

    }

          (4) 上传文件:

        File file = new File("/sdcard/test.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 {
                    
                }
            });
        }

         (5)下载文件:

        String url = "//img-my.csdn.net/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) {

            }

            @Override
            public void onResponse(Call call, Response response) {
                InputStream inputStream = response.body().byteStream();
                FileOutputStream fileOutputStream = null;
                try {
                    fileOutputStream = new FileOutputStream(new File("/sdcard/test.jpg"));
                    byte[] buffer = new byte[2048];
                    int len = 0;
                    while ((len = inputStream.read(buffer)) != -1) {
                        fileOutputStream.write(buffer, 0, len);
                    }
                    fileOutputStream.flush();
                } catch (IOException e) {

               }

               Log.d("andy", "文件下载成功");
           }
       });
   }

        (6)上传Multipart文件:

    MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
    RequestBody requestBody = new MultipartBody.Builder()
            .setType(MultipartBody.FORM)
            .addFormDataPart("title", "OkHttp3")
            .addFormDataPart("image", "test.jpg",
                    RequestBody.create(MEDIA_TYPE_PNG, new File("/sdcard/test.jpg")))
            .build();

    Request request = new Request.Builder()
            .header("Authorization", "Client-ID " + "...")
            .url("https://api.imgur.com/3/image")
            .post(requestBody)
            .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.i("andy", response.body().string());
       }
   });

         

4、源码分析

   Okhttp核心机制如下:

  • OkHttpClient
  • Request
  • Call
  • Dispatcher
  • Cache
  • Interceptor
  • StreamAllocation
  • RealConnection
  • ConnectionPool
  • HttpCodec
  • Response

4.1 OkHttpClient

 

 * <p>OkHttp performs best when you create a single {@code OkHttpClient} instance and reuse it for
 * all of your HTTP calls. This is because each client holds its own connection pool and thread
 * pools. Reusing connections and threads reduces latency and saves memory. Conversely, creating a
 * client for each request wastes resources on idle pools.

 

  • 用于设置一些公用属性,包括超时时间设置、缓存、连接池、是否支持重试等等,在网络请求的整个过程中,会经常调用到这些属性。采用构建者模式。

4.2 Request

  • 请求类,也采用构建者模式,用于设置Url、报文首部字段,报文主体;也可以设置头部"Cache-Control", 用于缓存策略的设置。

4.3 Dispatcher

  • 请求调度类,用于同步和异步请求的管理,主要用于异步请求的执行时间管理。

4.4 Call

  • 请求执行类,用于执行同步或异步网络请求,以及请求的状态判断,也可取消请求。

4.5 Cache

  • 缓存类,只支持“GET”方法的缓存,这里会缓存响应的头部信息和实体信息。

4.6 Interceptor

    拦截器就是基于责任链模式,每个节点有自己的职责,同时可以选择是否把任务传递给下一个环节。

    拦截器的设计,可以像工厂流水线一样,传递用户发起的请求 Request,每一个拦截器完成相应的功能,从失败重试和重定向实现、请求头的修改和Cookie 的处理,
缓存的处理,建立 TCP 和 SSH 连接,发送 Request 和读取 Response,每一个环节由专门的 Interceptor 负责。
  • RealCall:存储了固定顺序的拦截器列表:
  Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
  }
  • RetryAndFollowUpInterceptor

    用来实现连接失败的重试和重定向。

  • BridgeInterceptor

    用来修改请求和响应的header信息。

  • CacheInterceptor

    用来实现响应缓存。比如获取到的Response带有Date、Expires、Last-Modified、Etag等header,表示该Response可以缓存一段时间,下次请求就不需要

发送给服务端,直接从缓存获取。

  • ConnectInterceptor

    用来打开到服务器的连接。其实是调用StreamAllocation的newStream方法来打开连接的。TCP握手、TLS握手都发生在该阶段,过了这个阶段,和服务器端的

socket连接打通。

  • CallServerInterceptor

    用来向服务器发起请求并得到相应。上一个阶段已经握手成功,Stream已经打开,所以这个阶段把Request请求信息传入流中,并且从流中读取数据封装成Response返回。

  • 自定义拦截器LoggingInterceptor:  拦截发出的请求和传入的响应的日志.
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;
  }
}

 

4.7 RealConnection

    用来管理连接用的Socket。调用connect来进行Socket连接。

4.8 ConnectionPool

    连接池,保存RealConnection,用于连接池的复用,减少重新建立连接的开销。

4.9 StreamAllocation

    与一个Conncetion绑定。用来协调Conncetion、Stream、Call的关系,封装了网络连接创建的一些策略,比如使用 RouteSelector 和 RouteDatabase 在建联失败后的多 IP 路由的选择;

HttpStream 流的创建;Connection 的创建和使用 ConnectionPool 进行连接复用等。

4.10 HttpCodec

    编解码器。用来编码Http请求,解码Http响应。

4.11 Response

    网络请求响应,采用构建者模式。保存响应结果,包括响应的code、message、header、body信息。

 

5、网络请求优化点:

  • 连接复用节省连接建立时间,如开启keep-alive
  • 不用域名,用IP直连省去DNS解析过程,根据域名得到IP地址

6、Okhttp框架封装-Okgo

  • https://github.com/jeasonlzy/okhttp-OkGo