- 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 = "https://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