安卓发展的初期阶段,为测试安卓网络响应的功能,一群美国的安卓工程师跑到了网普遍不好的非洲,在那里弱网的情况下,试用应用程序。然后根据当地的实际情况,这群安卓工程师编写了出了这个非常有名的网络通讯库——OkHttp,专门用来解决弱网情景下的应用程序联网问题,取得了非常好的反响。
一、前期基础知识储备
为什么需要一个HTTP库?
(1)HttpURLConnection非常弱
起初Android系统提供了两种HTTP通信类,HttpURLConnection和HttpClient。后来谷歌在6.0版本删除了HttpClient相关API,所以Google在大部分安卓版本中推荐使用HttpURLConnection,但是这个类相比HttpClient实在是繁琐难用。
(2)OkHttp非常强大
后来移动支付Square公司贡献了OkHttp这一安卓最受欢迎的轻量级级框架,用于为广大开发者提供更好的网络请求方式。(Square还贡献了Retrofit/Picasso/LeakCanary)
OkHttp是一个相对成熟的网络请求解决方案, Android4.4的源码中可以看到HttpURLConnection已经替换成OkHttp实现了。所以我们更有理由相信OkHttp的强大。实际写过HttpURLConnection的人都知道多么麻烦!侧面对比一下,就懂了。
二、上代码 具体实现
build.gradle文件添加依赖
compile ‘com.squareup.okhttp3:okhttp:3.4.1’
okhttp的简单使用,主要包含:
- 一般的get请求
- 一般的post请求
- 基于Http的文件上传
- 文件下载
(1)实现一次OkHttp网络请求
简单实现一次OkHttp类型的网络请求分四步走:
1)创建OkHttpClient实例;
2)创建一个Request对象;
3)创建一个Call对象;
4)同步/异步发送网络请求
具体代码如下:
OkHttpClient client = new OkHttpClient();//创建OkHttpClient实例
Request request = new Request
.url("http://baidu.com");
.build(); //如果想要发起一条Http请求,首先需要创建一个Request对象
//调用OkHttpClient的newCall()方法来创建一个Call对象,并调用它的execute()方法发送请求|并获取服务器返回的数据
Response response = client.newCall(request).execute;
String responseData = response.body().string();
注意,这里Response对象就是服务器返回的数据,使用response.body().string()得到具体返回的内容,这里得到是字符类型;如果希望获得返回的二进制字节数组,则调用response.body().bytes();如果你想拿到返回的inputStream,则调用response.body().byteStream()。在得到的返回内容之后再调用XML/JSON解析方法进行解析返回数据的解析。
同时,还需注意,①OkHttp官方文档并不建议我们创建多个OkHttpClient,因此全局使用一个。 如果有需要,可以使用clone方法,再进行自定义;②执行网络请求的方法. execute只能执行一次。
以上实现的是GET同步请求,实现GET异步请求,只需要调用enqueue()方法即可,把这个请求,加入到队列当中,代码如下:
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if(response.isSuccessful()){//回调的方法执行在子线程。
//数据请求成功的处理
}
}
});
注:同步的请求方式,需要单独开启线程;而异步的网络线程不需要单独开启线程,逻辑会在enqueue()方法中调用好,但注意两种请求方式都不是在主线程中发生的,后面涉及到UI操作的都要返回到主线程中实现相关操作。
private void showResponse(final String response) {
runOnUiThread(new Runnable() {
@Override
public void run() {
// 在这里进行UI操作,将结果显示到界面上
responseText.setText(response);
}
});
(2)实现POST类型的网络请求
如果是发起一条POST请求会比GET请求稍微复杂一点,我们需要先构建出一个RequestBody对象用来存放待提交的餐具,而根据待提交的参数不同,又会有不同种类的RequestBody对象。
下面我们看一下Request.Builder类的post方法的声明:
public Builder post(RequestBody body)
由PSOT方法的声明可以看出,post方法接收的参数是RequestBody 对象,所以只要是RequestBody 类以及子类对象都可以当作参数进行传递。在开发中,我们可以直接使用的RequestBody对象有三种:
1)FormBody——传递键值对参数
这种方式用来上传String类型的键值对
private void postDataWithParame() {
OkHttpClient client = new OkHttpClient();//创建OkHttpClient对象。
FormBody.Builder formBody = new FormBody.Builder();//创建表单请求体
formBody.add("username","zhangsan");//传递键值对参数
Request request = new Request.Builder()//创建Request 对象。
.url("http://www.baidu.com")
.post(formBody.build())//传递请求体
.build();
client.newCall(request).enqueue(new Callback() {。。。});//此处省略回调方法。
}
2)RequestBody——上传Json或File对象
RequestBody是抽象类,故不能直接使用,但是他有静态方法create,使用这个方法可以得到RequestBody对象。这种方式可以上传Json对象或File对象
上传json对象使用示例如下:
OkHttpClient client = new OkHttpClient();//创建OkHttpClient对象。
MediaType JSON = MediaType.parse("application/json; charset=utf-8");//数据类型为json格式,
String jsonStr = "{\"username\":\"lisi\",\"nickname\":\"李四\"}";//json数据.
RequestBody body = RequestBody.create(JSON, josnStr);
Request request = new Request.Builder()
.url("http://www.baidu.com")
.post(body)
.build();
client.newCall(request).enqueue(new Callback() {。。。});//此处省略回调方法。
上传File对象使用示例如下:
OkHttpClient client = new OkHttpClient();//创建OkHttpClient对象。
MediaType fileType = MediaType.parse("File/*");//数据类型为json格式,
File file = new File("path");//file对象.
RequestBody body = RequestBody.create(fileType , file );
Request request = new Request.Builder()
.url("http://www.baidu.com")
.post(body)
.build();
client.newCall(request).enqueue(new Callback() {。。。});//此处省略回调方法。
3)MultipartBody——同时传递键值对参数和File对象
这个字面意思是多重的body。我们知道FromBody传递的是字符串型的键值对,RequestBody传递的是多媒体,那么如果我们想二者都传递怎么办?此时就需要使用MultipartBody类。
OkHttpClient client = new OkHttpClient();
MultipartBody multipartBody =new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("groupId",""+groupId)//添加键值对参数
.addFormDataPart("title","title")
.addFormDataPart("file",file.getName(),RequestBody.create(MediaType.parse("file/*"), file))//添加文件
.build();
final Request request = new Request.Builder()
.url(URLContant.CHAT_ROOM_SUBJECT_IMAGE)
.post(multipartBody)
.build();
client.newCall(request).enqueue(new Callback() {。。。});
(3)利用OkHttp返回的流对象进行下载文件
在OKHttp中并没有提供下载文件的功能,但是在Response中可以获取流对象,有了流对象我们就可以自己实现文件的下载。代码如下:
这段代码写在回调接口CallBack的onResponse方法中:
try{
InputStream is = response.body().byteStream();//从服务器得到输入流对象
long sum = 0;
File dir = new File(mDestFileDir);
if (!dir.exists()){
dir.mkdirs();
}
File file = new File(dir, mdestFileName);//根据目录和文件名得到file对象
FileOutputStream fos = new FileOutputStream(file);
byte[] buf = new byte[1024*8];
int len = 0;
while ((len = is.read(buf)) != -1){
fos.write(buf, 0, len);
}
fos.flush();
return file;
}
(4)对于OKHttp的使用封装
由于okhttp是偏底层的网络请求类库,返回结果的回调方法仍然执行在子线程中,需要自己跳转到UI线程,使用麻烦。为了使用方便需要对OKHttp进行再次封装。对于OKHttp的封装首推的就是hongyang大神的OKHttpUtils。
该封装库支持:
- 一般的get请求
- 一般的post请求
- 基于Http的文件上传
- 文件下载
- 上传下载的进度回调
- 加载图片
- 支持请求回调,直接返回对象、对象集合
- 支持session的保持
- 支持自签名网站https的访问,提供方法设置下证书就行
- 支持取消某个请求
总结:笔者之前写过四篇网络通讯库类型的文章,分别介绍了Volley和Retrofit,在其中也穿插的介绍了Volley、Retrofit和OkHttp的各自优劣。其实笔者最开始接触到的网络框架是OkHttp,当时正在研究Android提供的原生HTTPURLConnection的网络请求方式,学的真的是快失去信心,没有系统的学习过Http协议,然后学习起HTTPURLConnection真的是很难,最起码过程很漫长,后来接触到了OkHttp,瞬间被其简洁的代码,连贯的逻辑吸引了。所以现在,写这一篇OkHttp的文章用来和大家一起分享。
文章的最后分享几个链接,供读者参考:
OkHttp官网网址:OKHttp官网
了解源码可以在github上下载,地址是:https://github.com/square/okhttp
泡在网上的日子《OkHttp使用教程》