一、引言
OkHttp这个开源项目,可以说是如今最为流行的网络请求框架之一,由移动支付Square公司贡献(真的很NB,还贡献了Picasso和LeakCanary),用于替代HttpUrlConnection和Apache HttpClient (android API23 6.0里已移除HttpClient)。本文整理了 OkHttp 的使用方法,包括Get请求、Post请求、上传下载等功能。
二、OkHttp简介
OkHttp 是一款用于 Android 和 Java 的网络请求库,也是目前 Android 中最火的一个网络库。
OkHttp有很多的优点:
- 支持HTTP2/SPDY(SPDY是Google开发的基于TCP的传输层协议,用以最小化网络延迟,提升网络速度,优化用户的网络使用体验。)
- socket自动选择最好路线,并支持自动重连,拥有自动维护的socket连接池,减少握手次数,减少了请求延迟,共享Socket,减少对服务器的请求次数
- 基于Headers的缓存策略减少重复的网络请求
- 拥有Interceptors轻松处理请求与响应(自动处理GZip压缩)
OkHttp的作用:
- PUT,DELETE,POST,GET等请求
- 文件的上传下载
- 加载图片(内部会图片大小自动压缩)
- 支持请求回调,直接返回对象、对象集合
- 支持session的保持
HttpURLConnection和OkHttp的比较:
- HttpURLConnection有的API,OkHttp基本上都有
- HttpURLConnection和OkHttp都支持Https,流的上传和下载,超时,IP6、连接池等等
- OkHttp比HttpURLConnection具有更好的同步异步请求、缓存机制,支持HttpDNS、重定向、Gzip压缩,平台适应性、很好的服务器IP的转换、直接Socket通信,支持拦截器等等
和其它网络框架类似,OKhttp也主要由以下6个概念一起运作:
- OkHttpClient:客户端
- Dispatcher:线程池
- Interceptor:拦截器(OKhttp的特色)
- Request:请求
- Response:响应
- CallBack:回调
感觉真的很强大!OkHttp对于网络请求这块使应用更加省流量、请求的更快。OkHttp对于Https和HttpDNS的支持,使得应用的网络通信安全性更高。当然了,万事都有不足的地方,例如:
- OkHttp不支持优先级请求
- OkHttp不支持自签名证书
- OkHttp header中不能传中文
三、OkHttp的常见用法
3.1 加载
在使用前需要先在项目中添加OkHttp的依赖库,在对应的Module的gradle中添加如下语句
implementation 'com.squareup.okhttp3:okhttp:3.11.0'
此外,OkHttp内部依赖另一个开源库OkIo,所以也要将它导入进来
implementation 'com.squareup.okio:okio:1.15.0'
3.2 Get请求流程
最常见的网络请求方式可以说就是Get请求了,这里来获取我的个人主页的网页内容:
- 创建OkHttpClient对象
- 通过Builder模式创建Request对象,参数必须有个url参数,可以通过Request.Builder设置更多的参数比如:header、method等
- 通过request的对象去构造得到一个Call对象,Call对象有execute()和cancel()等方法。
- 以异步的方式去执行请求,调用的是call.enqueue,将call加入调度队列,任务执行完成会在Callback中得到结果。
OkHttpClient client = new OkHttpClient();
//构造Request对象
//采用建造者模式,链式调用指明进行Get请求,传入Get的请求地址
Request request = new Request.Builder().get().url("http://www.baidu.co").build();
Call call = client.newCall(request);
//异步调用并设置回调函数
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//toast
}
@Override
public void onResponse(Call call, final Response response) throws IOException {
final String responseStr = response.body().string();
runOnUiThread(new Runnable() {
@Override
public void run() {
//UI updata
}
});
}
});
注意事项:
- 异步调用的回调函数是在子线程,我们不能在子线程更新UI,需要借助于 runOnUiThread() 方法或者 Handler 来处理
- onResponse回调有一个参数是response,如果我们想获得返回的是字符串,可以通过response.body().string()获取;如果希望获得返回的二进制字节数组,则调用response.body().bytes();如果你想拿到返回的inputStream,则调response.body().byteStream(),有inputStream我们就可以通过IO的方式写文件
Get请求也有同步调用,但一般使用的比较少:
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).execute();
String string = response.body().string();
System.out.println(string);
或
//创建OkHttpClient对象
OkHttpClient okHttpClient = new OkHttpClient();
//创建Request对象,设置一个url地址(个人站点),设置请求方式。
Request request = new Request.Builder().url("http://www.xmamiga.com").method("GET",null).build();
//创建一个call对象,参数就是Request请求对象
Call call = okHttpClient.newCall(request);
//同步调用会阻塞主线程,这边在子线程进行
new Thread(new Runnable() {
@Override
public void run() {
try {
//同步调用,返回Response,会抛出IO异常
Response response = call.execute();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
同步GET请求和异步GET请求基本一样,不同地方是同步请求调用Call的execute()方法,而异步请求调用call.enqueue()方法。
下载文件也是我们经常用到的功能,以下载图片为例:
OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder().url("https://www.xmamiga.com/img/tuoxiang.png").get().build();
Call call = okHttpClient.newCall(request);
//请求加入调度,重写回调方法
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e(TAG, "onFailure: "+call.toString() );
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//拿到字节流
InputStream is = response.body().byteStream();
int len = 0;
//设置下载图片存储路径和名称
File file = new File(Environment.getExternalStorageDirectory(),"tuoxiang.png");
FileOutputStream fos = new FileOutputStream(file);
byte[] buf = new byte[128];
while((len = is.read(buf))!= -1){
fos.write(buf,0,len);
Log.e(TAG, "onResponse: "+len );
}
fos.flush();
fos.close();
is.close();
}
});
3.3 POST请求流程
流程和GET请求类似,不同的地方在于:
- 需要RequestBody请求体封装各种类型的请求参数
- 调用Request.Builder的post()方法,传入RequestBody,设置为POST请求
在OkHttp中用Post方法向服务器发送一个请求体时,请求体需要是一个RequestBody。这个请求体可以是:
- key-value:键值对类型
- String:字符串类型
- Form:类似于Html的表单数据提交
- Stream:流类型
- File:文件类型
3.3.1 Post请求提交键值对
- 创建OkHttpClient对象
- 通过new FormBody()调用build方法,创建一个RequestBody,可以用add添加键值对 ,FormBody 是 RequestBody 的子类
- 创建Request对象,设置URL地址,将RequestBody作为post方法的参数传入
- 创建一个call对象,参数就是Request请求对象
- 请求加入调度,重写回调方法
OkHttpClient client = new OkHttpClient();
//构建FormBody,传入要提交的参数
FormBody formBody = new FormBody
.Builder()
.add("username", "initObject")
.add("password", "initObject")
.build();
final Request request = new Request.Builder()
.url("http://www.xmamiga.com/")
.post(formBody)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//toast
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final String responseStr = response.body().string();
//String.valueOf(response.code());
runOnUiThread(new Runnable() {
@Override
public void run() {
// UI updata
}
});
}
});
3.3.2 Post请求字符串
有时候我们会有要传送字符串的需要,比如向服务器发送一个JSON字符串。那么就可以用如下方法:
OkHttpClient client = new OkHttpClient();
//RequestBody中的MediaType指定为纯文本,编码方式是utf-8
RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain;charset=utf-8"),
"{username:admin;password:admin}");
final Request request = new Request.Builder()
.url("http://www.xmamiga.com/")
.post(requestBody)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//toast
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final String responseStr = response.body().string();
// String.valueOf(response.code());
runOnUiThread(new Runnable() {
@Override
public void run() {
// UI updata
}
});
}
});
3.3.3 Post 上传文件
文件的类型很多,有图片,TXT文档等,不同地方主要是RequestBody,以上传图片为例,代码如下:
OkHttpClient okHttpClient = new OkHttpClient();
//上传的图片
File file = new File(Environment.getExternalStorageDirectory(), "tuoxiang.png");
//通过RequestBody.create 创建requestBody对象,application/octet-stream 表示文件是任意二进制数据流
RequestBody requestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
//创建Request对象,设置URL地址,将RequestBody作为post方法的参数传入
Request request = new Request.Builder().url("url").post(requestBody).build();
//创建一个call对象,参数就是Request请求对象
Call call = okHttpClient.newCall(request);
//请求加入调度,重写回调方法
call.enqueue(new Callback() {
@Override
public void onFailure (Call call, IOException e){
//toast
}
@Override
public void onResponse (Call call, Response response) throws IOException {
//TODO
}
});
3.3.4 Post 表单
有些情况下既要上传文件还要上传其他类型字段。比如在个人中心我们可以修改名字,年龄,修改图像,这其实就是一个表单。这里我们用到MuiltipartBody ,它 是RequestBody 的一个子类,我们提交表单就是利用这个类来构建一个 RequestBody,代码如下:
OkHttpClient okHttpClient = new OkHttpClient();
//上传的图片
File file = new File(Environment.getExternalStorageDirectory(), "tuoxiang.png");
//通过new MultipartBody build() 创建requestBody对象,
RequestBody requestBody = new MultipartBody.Builder()
//设置类型是表单
.setType(MultipartBody.FORM)
//添加数据
.addFormDataPart("username","xmamiga")
.addFormDataPart("age","25")
.addFormDataPart("image","tuoxiang.png",
RequestBody.create(MediaType.parse("image/png"),file))
.build();
//创建Request对象,设置URL地址,将RequestBody作为post方法的参数传入
Request request = new Request.Builder().url("url").post(requestBody).build();
//创建一个call对象,参数就是Request请求对象
Call call = okHttpClient.newCall(request);
//请求加入调度,重写回调方法
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
3.4 取消请求
开发中,有时需要取消请求,则OkHttp也提供了取接口:
call.cancel();//取消请求,不能取消已经准备完成的请求
okHttpClient.dispatcher().cancelAll();//取消所有请求
3.5 附加参数使用
为了多样化调用,OkHttp提供了附加参数的设置。
3.5.1 设置请求头
Request request = new Request.Builder()
.url(url)
.header("headerKey1", "headerValue1")// 设置请求头
.headers(Headers headers)// 批量设置请求头
.addHeader("headerKey2", "headerValue2")// 追加请求头
.removeHeader("headerKey2")//去除请求头
.build();
3.5.2 获取请求头
Response response=okHttpClient.newCall(request).execute();
response.header("responseHeader");
response.header("responseHeader", "defaultValue");
//
Headers headers = response.headers();
3.5.3 设置请求超时
okHttpClient = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
3.5.4 设置缓存
File sdcache = getExternalCacheDir();
int cacheSize = 10 * 1024 * 1024;
okHttpClient = new OkHttpClient.Builder()
.cache(new Cache(sdcache.getAbsoluteFile(), cacheSize));
.build();
四、总结
由于OkHttp是偏底层的网络请求类库,返回结果的回调方法仍然执行在子线程中,需要自己跳转到UI线程,使用麻烦。为了使用方便需要对OKHttp进行再次封装,这就有了Retrofit(热豆腐)。