引言
最初我们进行HTTP请求时使用的是HttpURLConnection或者HttpClient,那么这两者都有什么优缺点呢?
HttpClient是Apache基金会的一个开源网络库,功能十分强大,API数量众多,但正是由于庞大的API数量使得我们很难在不破坏兼容性的情况下对它进行升级和扩展,所以Android团队在提升和优化HttpClient方面的工作态度并不积极。官方在Android 2.3以后就不建议用了,并且在Android 5.0以后废弃了HttpClient,在Android 6.0更是删除了HttpClient。
HttpURLConnection是一种多用途、轻量极的HTTP客户端,提供的API比较简单,可以容易地去使用和扩展。不过在Android 2.2版本之前,HttpURLConnection一直存在着一些令人厌烦的bug。比如说对一个可读的InputStream调用close()方法时,就有可能会导致连接池失效了。那么我们通常的解决办法就是直接禁用掉连接池的功能:
private void disableConnectionReuseIfNecessary() {
// 这是一个2.2版本之前的bug
if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) {
System.setProperty("http.keepAlive", "false");
}
}
因此一般推荐是在2.2之前使用HttpClient,因为其bug较少。在2.2之后推荐使用HttpURLConnection,因为API简单、体积小、有压缩和缓存机制,并且Android团队后续会继续优化HttpURLConnection。
但是上面两个类库和OkHttp比起来就显得有些不足了,因为OkHttp不仅具有高效的请求效率,并且提供了很多开箱即用的网络疑难杂症解决方案。
简介
从Android 4.4开始google已经开始将源码中的HttpURLConnection替换为OkHttp,而在Android 6.0之后的SDK中google更是移除了对于HttpClient的支持,而现在流行的Retrofit同样是使用OkHttp进行再次封装而来的。
OkHttp是一个快速、高效的网络请求库,它的设计和实现的首要目标便是高效,有如下特性:
- 支持http2,使得对同一个主机发出的所有请求都可以共享相同的socket套接字连接;
- 使用连接池来复用连接以减少延迟、提高效率;
- 支持Gzip压缩响应体,降低传输内容的大小;
- 支持Http缓存,避免重复请求;
- 请求失败时会自动重试主机中的其他IP地址自动重定向;
- 使用Okio来简化数据的访问与存储,提高性能;
基本使用
OKHttp3同步的使用方法
OkHttpClient okHttpClient = new OkHttpClient();//1.定义一个client
Request request = new Request.Builder().url ("http://www.baidu.com").build();//2.定义一个request
Call call = okHttpClient.newCall(request);//3.使用client去请求
try {
String result = call.execute().body().string();//4.获得返回结果
System.out.println(result);
} catch (IOException e) {
e.printStackTrace();
}
OKHttp3异步的使用方法
OkHttpClient okHttpClient = new OkHttpClient();//1.定义一个client
Request request = new Request.Builder().url ("http://www.baidu.com").build();//2.定义一个request
Call call = okHttpClient.newCall(request);//3.使用client去请求
call.enqueue(new Callback() {//4.回调方法
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String result = response.body().string();//5.获得网络数据
System.out.println(result);
}
});
拦截器
Application interceptors应用程序拦截器
Interceptor appInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
//---------请求之前------------
Response response = chain.proceed(request1);
//---------请求之后------------
return response;
}
};
- 不需要担心比如重定向和重试的中间响应。
- 总是被调用一次,即使HTTP响应结果是从缓存中获取的。
- 监控应用程序的原始意图。不关心例如OkHttp注入的头部字段If-None-Match。
- 允许短路,不调用Chain.proceed()。
- 允许重试并多次调用Chain.proceed()。
Network Interceptors网络拦截器
Interceptor networkInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
//---------请求之前-----
Log.d(TAG,"network interceptor:begin");
Response response = chain.proceed(request);
Log.d(TAG,"network interceptor:end");
return response;
}
};
- 能够对中间的响应进行操作比如重定向和重试。
- 当发生网络短路时,不调用缓存的响应结果。
- 监控数据,就像数据再网络上传输一样。
- 访问承载请求的连接Connection。
配置拦截器
okHttpClient = new OkHttpClient
.Builder()
.addInterceptor(appInterceptor)//Application拦截器,主要拦截日志
.addNetworkInterceptor(networkInterceptor)//Network拦截器,主要拦截请求头
.build();
缓存
如果服务器支持缓存,请求返回的Response会带有这样的Header:Cache-Control, max-age=xxx,这种情况下我们只需要手动给okhttp设置缓存就可以让okhttp自动帮你缓存了。这里的max-age的值代表了缓存在你本地存放的时间。
OkHttpClient okHttpClient = new OkHttpClient();
OkHttpClient newClient = okHttpClient.newBuilder()
.cache(new Cache(mContext.getCacheDir(), 10*1024*1024))
.connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.build();
如果服务器不支持缓存就可能没有指定这个头部,这种情况下我们就需要使用Interceptor来重写Respose的头部信息,从而让okhttp支持缓存。
Interceptor interceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
String cacheControl = request.cacheControl().toString();
if (TextUtils.isEmpty(cacheControl)) {
cacheControl = "public, max-age=60";
}
return response.newBuilder()
.header("Cache-Control", cacheControl)
.removeHeader("Pragma")
.build();
}
};
//设置缓存路径
File httpCacheDirectory = new File(mContext.getCacheDir(), "responses");
//设置缓存 10M
Cache cache = new Cache(httpCacheDirectory, 10 * 1024 * 1024);
//创建OkHttpClient,并添加拦截器和缓存代码
OkHttpClient client = new OkHttpClient.Builder()
.addNetworkInterceptor(interceptor)
.cache(cache)
.build();
断点续传
//服务器返回的内容的总长度
final long contentLength = response.body().contentLength();
//获取服务器返回的输入流
InputStream inputStream = response.body().byteStream();
//随机访问流
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
//在文件的什么位置开始追加写入 类似快进
randomAccessFile.seek(downloadLen);
byte[] bytes = new byte[1024];
int len;
//最主要就是下次读的时候从什么时候开始
while ((len = inputStream.read(bytes)) != -1) {
downloadLen = downloadLen + len;
randomAccessFile.write(bytes, 0, len);
}
inputStream.close();
randomAccessFile.close();
解析数据泛型
private T parseData(String json, OkHttpListener okHttpListener) {
//得到callback这个类上实现的所有接口包括泛型
Type[] interfaces = okHttpListener.getClass().getGenericInterfaces();
//interfaces[0] 取出数组中的第一个 getActualTypeArguments 获取真实的类型
Type[] arguments = ((ParameterizedType) interfaces[0]).getActualTypeArguments();
Gson gson = new Gson();
T t = gson.fromJson(json, arguments[0]);
return t;
}
大概的内容就到这儿了,希望小伙伴可以更深层次的挖掘!