一,OKHttp 介绍
okhttp 是一个第三方类库,用于 android 中请求网络。
这是一个开源项目,是安卓端最火热的轻量级框架,由移动支付 Square 公司贡献(该公司还贡献了 Picasso 和 LeakCanary) 。用于替代 HttpUrlConnection 和 Apache HttpClient(android API23 里已移除 HttpClient)。
okhttp 有自己的官网,官网网址:OKHttp 官网
如果想了解原码可以在 github 上下载,地址是:https://github.com/square/okhttp
在 AndroidStudio 中使用不需要下载 jar 包,直接添加依赖即可:
compile ‘com.squareup.okhttp3:okhttp:3.4.1’
下面对以 OKHttp3 来详细介绍 OKHttp 的使用方法。
二,get 请求的使用方法
使用 OKHttp 进行网络请求支持两种方式,一种是同步请求,一种是异步请求。下面分情况进行介绍。
1,get 的同步请求
对于同步请求在请求时需要开启子线程,请求成功后需要跳转到 UI 线程修改 UI。
使用示例如下:
public void getDatasync(){ new Thread(new Runnable() { @Override public void run() { try { OkHttpClient client = new OkHttpClient();//创建OkHttpClient对象 Request request = new Request.Builder() .url("http://www.baidu.com")//请求接口。如果需要传参拼接到接口后面。 .build();//创建Request 对象 Response response = null; response = client.newCall(request).execute();//得到Response 对象 if (response.isSuccessful()) { Log.d("kwwl","response.code()=="+response.code()); Log.d("kwwl","response.message()=="+response.message()); Log.d("kwwl","res=="+response.body().string()); //此时的代码执行在子线程,修改UI的操作请使用handler跳转到UI线程。 } } catch (Exception e) { e.printStackTrace(); } } }).start();}
此时打印结果如下:
response.code()==200;
response.message()==OK;
res=={“code”:200,”message”:success};
注意事项:
1,Response.code 是 http 响应行中的 code,如果访问成功则返回 200.这个不是服务器设置的,而是 http 协议中自带的。res 中的 code 才是服务器设置的。注意二者的区别。
2,response.body().string()本质是输入流的读操作,所以它还是网络请求的一部分,所以这行代码必须放在子线程。
3,response.body().string()只能调用一次,在第一次时有返回值,第二次再调用时将会返回 null。原因是:response.body().string()的本质是输入流的读操作,必须有服务器的输出流的写操作时客户端的读操作才能得到数据。而服务器的写操作只执行一次,所以客户端的读操作也只能执行一次,第二次将返回 null。
2,get 的异步请求
这种方式不用再次开启子线程,但回调方法是执行在子线程中,所以在更新 UI 时还要跳转到 UI 线程中。
使用示例如下:
private void getDataAsync() { OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url("http://www.baidu.com") .build(); 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()){//回调的方法执行在子线程。 Log.d("kwwl","获取数据成功了"); Log.d("kwwl","response.code()=="+response.code()); Log.d("kwwl","response.body().string()=="+response.body().string()); } } });}
异步请求的打印结果与注意事项与同步请求时相同。最大的不同点就是异步请求不需要开启子线程,enqueue 方法会自动将网络请求部分放入子线程中执行。
注意事项:
1,回调接口的 onFailure 方法和 onResponse 执行在子线程。
2,response.body().string()方法也必须放在子线程中。当执行这行代码得到结果后,再跳转到 UI 线程修改 UI。
三,post 请求的使用方法
Post 请求也分同步和异步两种方式,同步与异步的区别和 get 方法类似,所以此时只讲解 post 异步请求的使用方法。
使用示例如下:
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() {。。。});//回调方法的使用与get异步请求相同,此时略。}
看完代码我们会发现:post 请求中并没有设置请求方式为 POST,回忆在 get 请求中也没有设置请求方式为 GET,那么是怎么区分请求方式的呢?重点是 Request.Builder 类的 post 方法,在 Request.Builder 对象创建最初默认是 get 请求,所以在 get 请求中不需要设置请求方式,当调用 post 方法时把请求方式修改为 POST。所以此时为 POST 请求。
四,POST 请求传递参数的方法总结
在 post 请求使用方法中讲了一种传递参数的方法,就是创建表单请求体对象,然后把表单请求体对象作为 post 方法的参数。post 请求传递参数的方法还有很多种,但都是通过 post 方法传递的。下面我们看一下 Request.Builder 类的 post 方法的声明:
public Builder post(RequestBody body)
由方法的声明可以看出,post 方法接收的参数是 RequestBody 对象,所以只要是 RequestBody 类以及子类对象都可以当作参数进行传递。FormBody 就是 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() {。。。});
4,自定义 RequestBody 实现流的上传
在上面的分析中我们知道,只要是 RequestBody 类以及子类都可以作为 post 方法的参数,下面我们就自定义一个类,继承 RequestBody,实现流的上传。
使用示例如下:
首先创建一个 RequestBody 类的子类对象:
RequestBody body = new RequestBody() { @Override public MediaType contentType() { return null; } @Override public void writeTo(BufferedSink sink) throws IOException {//重写writeTo方法 FileInputStream fio= new FileInputStream(new File("fileName")); byte[] buffer = new byte[1024*8]; if(fio.read(buffer) != -1){ sink.write(buffer); } }};
然后使用 body 对象:
OkHttpClient client = new OkHttpClient();//创建OkHttpClient对象。Request request = new Request.Builder() .url("http://www.baidu.com") .post(body) .build();client.newCall(request).enqueue(new Callback() {。。。});
以上代码的与众不同就是 body 对象,这个 body 对象重写了 write 方法,里面有个 sink 对象。这个是 OKio 包中的输出流,有 write 方法。使用这个方法我们可以实现上传流的功能。
使用 RequestBody 上传文件时,并没有实现断点续传的功能。我可以使用这种方法结合 RandomAccessFile 类实现断点续传的功能。
五,设置请求头
OKHttp 中设置请求头特别简单,在创建 request 对象时调用一个方法即可。
使用示例如下:
Request request = new Request.Builder() .url("http://www.baidu.com") .header("User-Agent", "OkHttp Headers.java") .addHeader("token", "myToken") .build();
其他部分代码略。
六,下载文件
在 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; }
七,对于 OKHttp 的使用封装
由于 okhttp 是偏底层的网络请求类库,返回结果的回调方法仍然执行在子线程中,需要自己跳转到 UI 线程,使用麻烦。为了使用方便需要对 OKHttp 进行再次封装。对于 OKHttp 的封装首推的就是 hongyang 大神的 OKHttpUtils。我个人在看过 OKHttp 的原码和借鉴各大神的封装源码后封装了一套自己的 OKHttpUtils。这套 OKHttpUtils 最大的优点是简单和便于使用,这是我项目中实际用的网络请求工具类,完全可以说拿来即用。而且代码简单,可供学习使用。
github 的地址是:https://github.com/guozhengXia/OkHttpUtils
封装的功能有:
- 一般的 get 请求
- 一般的 post 请求
- 上传单个文件(包含进度)
- 上传 list 集合文件
- 上传 map 集合文件
- 文件下载(包含进度)
- 图片下载(实现了图片的压缩)
请大家多多支持,多多提出宝贵意见
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于