网络通讯库OkHttp精炼详解
安卓发展的初期阶段,为测试安卓网络响应的功能,一群美国的安卓工程师跑到了网普遍不好的非洲,在那里弱网的情况下,试用应用程序。然后根据当地的实际情况,这群安卓工程师编写了出了这个非常有名的网络通讯库——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("https://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("https://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("https://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("https://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,瞬间被其简洁的代码,连贯的逻辑吸引了。