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