【185】okHttp 3.0 基本使用整理
0.okHttp点滴
【1】网络请求属于耗时请求,不能放在ui线程(主线程)执行,需要放在子线程中执行。
1.GET请求--默认请求
1 /** 2 * [1]GET请求 3 */ 4 private void okHttpGet(String url) throws IOException { 5 //1,创建okHttpClient对象 6 OkHttpClient client = new OkHttpClient(); 7 8 9 //2,创建一个Request 10 //可以通过Request.Builder设置更多的参数比如: header、 method等 11 Request request = new Request.Builder() 12 .get() //get请求; 13 .url(url) 14 .build(); 15 16 //3,新建一个call对象 17 Call call = client.newCall(request); 18 //[4-1]同步请求-会阻塞,使用很少; 19 call.execute(); 20 //[4-2]异步请求-请求是在线程,更新数据需要使用handler等; 21 //请求加入调度,这里是异步Get请求回调 22 call.enqueue(new Callback() { 23 @Override 24 public void onFailure(Call call, IOException e) { 25 26 } 27 28 @Override 29 public void onResponse(Call call, Response response) throws IOException { 30 //可以判断response返回的状态: 31 if (response.isSuccessful()) { 32 //response.body返回数据; 33 //[1] 34 String string = response.body().string(); 35 // [2] 36 byte[] bytes = response.body().bytes(); 37 // [3] 38 InputStream inputStream = response.body().byteStream(); 39 // [4] 40 Reader reader = response.body().charStream(); 41 // [5] 42 BufferedSource source = response.body().source(); 43 } 44 45 } 46 }); 47 48 }
【说明】
【1】发送一个GET请求的步骤,首先构造一个Request对象,参数至少具备URL,当然也可以通过Request.Builder设置更多的参数比如:header、method等。
1 //URL带的参数 2 HashMap<String,String> params = new HashMap<>(); 3 //GET 请求带的Header 4 HashMap<String,String> headers= new HashMap<>(); 5 //HttpUrl.Builder构造带参数url 6 HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder(); 7 if (params != null) { 8 9 for (String key : params.keySet()) { 10 urlBuilder.setQueryParameter(key, params.get(key)); 11 } 12 } 13 Request request = new Request.Builder() 14 .url(urlBuilder.build()) 15 .headers(headers == null ? new Headers.Builder().build() : Headers.of(headers)) 16 .get() 17 .build();
【2】通过Request的对象去构造得到一个Call对象,类似于将你的请求封装成了任务,既然是任务,就会有execute(),enqueue()和cancel()等方法。
execute():同步GET请求
1 //同步 2 Response response = call.execute() 3 if(response.isSuccessful()){ 4 //响应成功 5 }
enqueue():异步GET请求,将call加入调度队列,然后等待任务执行完成,在Callback中即可得到结果。
1 call.enqueue(new Callback() { 2 @Override 3 public void onFailure(Call call, IOException e) { 4 5 } 6 7 @Override 8 public void onResponse(Call call, Response response) throws IOException { 9 //可以判断response返回的状态: 10 if (response.isSuccessful()) { 11 //response.body返回数据; 12 } 13 14 } 15 });
cancel():Call请求的取消,okHttp支持请求取消功能,当调用请求的cancel()时,请求就会被取消,抛出异常。
又是需要监控许多Http请求的执行情况,可以把这些请求的Call搜集起来,
执行完毕自动剔除,如果在请求执行过程中(如下载),想取消执行,可使用call.cancel()取消。
【3】请求的响应Response
对于同步GET请求,Response对象是直接返回的。
异步GET请求,通过onResponse回调方法传参数,需要注意的是这个onResponse回调方法不是在主线程回调,可以使用runInUIThread(new Runnable(){})。
获得,可以通过 response.body().string() 获取;
1 //[4-2]异步请求-请求是在线程,更新数据需要使用handler等; 2 //请求加入调度,这里是异步Get请求回调 3 call.enqueue(new Callback() { 4 @Override 5 public void onFailure(Call call, IOException e) { 6 7 } 8 9 @Override 10 public void onResponse(Call call, Response response) throws IOException { 11 //可以判断response返回的状态: 12 if (response.isSuccessful()) { 13 //response.body返回数据; 14 //[1]返回的字符串 15 String string = response.body().string(); 16 //[2]返回的二进制字节数组 17 byte[] bytes = response.body().bytes(); 18 //[3]返回的inputStream 19 InputStream inputStream = response.body().byteStream(); 20 //[4] 21 Reader reader = response.body().charStream(); 22 //[5] 23 BufferedSource source = response.body().source(); 24 } 26 } 27 });
2.post 提交键值对
【post创建的说明】
post请求创建request和get是一样的,只是post请求需要提交一个表单,就是RequestBody。表单的格式有好多种。
【提交键值对】
1 /** 2 * [2]post 提交键值对 3 */ 4 private void okHttpPostWithParam(String url) { 5 OkHttpClient okHttpClient = new OkHttpClient(); 6 7 /*okhttp2 的写法; 8 FormEncodingBuilder builder = new FormEncodingBuilder(); 9 builder.add("username", "CodingMankk");*/ 10 11 //okhttp3的写法 12 FormBody formBody = new FormBody.Builder() 13 .add("username", "admin") 14 .add("password", "admin") 15 .build(); 16 17 Request request = new Request.Builder() 18 .url(url) 19 .post(formBody) 20 .build(); 21 22 Call call = okHttpClient.newCall(request); 23 24 //[1]同步请求 25 /*try { 26 call.execute(); 27 } catch (IOException e) { 28 e.printStackTrace(); 29 }*/ 30 31 //[2]异步请求 32 call.enqueue(new Callback() { 33 @Override 34 public void onFailure(Call call, IOException e) { 35 //请求失败的回调 36 } 37 38 @Override 39 public void onResponse(Call call, Response response) throws IOException { 40 //请求成功的回调; 41 // response 是请求到的数据; 42 } 43 }); 44 }
3.post 提交表单
使用 FormEncodingBuilder(2.0)FormBody(3.0) 来构建和HTML <form> 标签相同效果的请求体。
用户注册的情况,需要你输入用户名,密码,还有上传头像,这其实就是一个表单,
主要的区别就在于构造不同的RequestBody传递给post方法即可
表单数据的提交需要:compile 'com.squareup.okio:okio:1.11.0'
MuiltipartBody,是RequestBody的一个子类,提交表单利用MuiltipartBody来构建一个RequestBody,
下面的代码是发送一个包含用户民、密码、头像的表单到服务端
【注意】
【1】如果提交的是表单,一定要设置setType(MultipartBody.FORM)这一句
【2】 addFormDataPart("avatar","avatar.png",requestBodyFile)
参数1:类似于键值对的键,是供服务端使用的,就类似于网页表单里面的name属性,<input type="file" name="myfile">
参数2:本地文件的名字
参数3:第三个参数是RequestBody,里面包含了我们要上传的文件的路径以及MidiaType
【3】本地sd卡的读写权限的添加:<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
1 private void okHttpPostFormData(String url) { 2 //构造图片文件; 3 File file = new File(Environment.getExternalStorageDirectory(), "avatar.png"); 4 if (!file.exists()) { 5 Log.d("okHttpPostFormData", "文件不存在"); 6 return; 7 } 8 9 RequestBody requestBodyFile = 10 RequestBody.create(MediaType.parse("application/octet-stream"), file); 11 12 MultipartBody multipartBody = new MultipartBody.Builder() 13 //一定要设置这句 14 .setType(MultipartBody.FORM) 15 .addFormDataPart("username", "admin") 16 .addFormDataPart("password", "admin") 17 .addFormDataPart("avatar", "avatar.png", requestBodyFile) 18 .build(); 19 20 Request request = new Request.Builder() 21 .url(url) 22 .post(multipartBody) 23 .build(); 24 25 OkHttpClient okHttpClient = new OkHttpClient(); 26 Call call = okHttpClient.newCall(request); 27 //[1]同步请求; 28 /* try { 29 call.execute(); 30 } catch (IOException e) { 31 e.printStackTrace(); 32 }*/ 33 34 //[2]异步请求 35 call.enqueue(new Callback() { 36 @Override 37 public void onFailure(Call call, IOException e) { 38 39 } 40 41 @Override 42 public void onResponse(Call call, Response response) throws IOException { 43 44 } 45 }); 46 47 }
【多种类型的multipartBody的构造】
【1】图片文件+字符串;
1 //构造图片文件; 2 File file = new File(Environment.getExternalStorageDirectory(), "avatar.png"); 3 if (!file.exists()) { 4 Log.d("okHttpPostFormData", "文件不存在"); 5 return; 6 } 7 8 RequestBody requestBodyFile = 9 RequestBody.create(MediaType.parse("application/octet-stream"), file); 10 11 MultipartBody multipartBody = new MultipartBody.Builder() 12 //一定要设置这句 13 .setType(MultipartBody.FORM) 14 .addFormDataPart("username", "admin") 15 .addFormDataPart("password", "admin") 16 .addFormDataPart("avatar", "avatar.png", requestBodyFile) 17 .build(); 18 19 Request request = new Request.Builder() 20 .url(url) 21 .post(multipartBody) 22 .build();
【2】多个string字符串
Post的时候,参数是包含在请求体中的,通过MultipartBody.Builder 添加多个String键值对,然后去构造RequestBody,最后完成Request的构造
1 //POST参数构造MultipartBody.Builder,表单提交 2 HashMap<String, String> params = new HashMap<>(); 3 MultipartBody.Builder MultipartBodyBuilder = new MultipartBody.Builder() 4 .setType(MultipartBody.FORM); 5 if (params != null) { 6 for (String key : params.keySet()) { 7 if (params.get(key) != null) { 8 MultipartBodyBuilder.addFormDataPart(key, params.get(key)); 9 } 10 } 11 } 12 13 // 构造Request->call->执行 14 Request request = new Request.Builder() 15 .url(url) 16 .post(MultipartBodyBuilder.build())//参数放在body体里 17 .build();
3.post 文件上传
3.1 RequestBody的构造
上传文件本身也是一个POST请求。在上面的POST请求中可以知道,POST请求的所有参数都是在BODY体中的,
请求体的源码RequestBody:请求体=contentType +BufferedSink
【RequestBody源码】
【简析】源码中看出,该类是一个抽象类,需要实现;其中具有三个抽象方法;下面是5个Request的create方法。
1 //抽象类请求体,**请求体=contentType + BufferedSink** 2 public abstract class RequestBody { 3 /** Returns the Content-Type header for this body. */ 4 //【抽象方法1】返回Body体的内容类型 5 public abstract @Nullable MediaType contentType(); 7 /** 8 * Returns the number of bytes that will be written to {@code sink} in a call to {@link #writeTo}, 9 * or -1 if that count is unknown. 10 */ 11 //【抽象方法2】返回写入sink的字节长度 12 public long contentLength() throws IOException { 13 return -1; 14 } 15 16 /** Writes the content of this request to {@code sink}. */ 17 //【抽象方法3】写入缓存sink 18 public abstract void writeTo(BufferedSink sink) throws IOException; 19 20 /** 21 * Returns a new request body that transmits {@code content}. If {@code contentType} is non-null 22 * and lacks a charset, this will use UTF-8. 23 */ 24 //【创建的请求体1】创建一个请求体,如果contentType不等于null且缺少字符集,将使用UTF-8 25 public static RequestBody create(@Nullable MediaType contentType, String content) { 26 Charset charset = Util.UTF_8; 27 if (contentType != null) { 28 //contentType里面的字符集 29 charset = contentType.charset(); 30 if (charset == null) { 31 charset = Util.UTF_8; 32 //contentType 里面加入字符集 33 contentType = MediaType.parse(contentType + "; charset=utf-8"); 34 } 35 } 36 //按字符集变成字节 37 byte[] bytes = content.getBytes(charset); 38 return create(contentType, bytes); 39 } 40 41 /** Returns a new request body that transmits {@code content}. */ 42 //【创建的请求体2】创建新的请求体,传输字节 43 public static RequestBody create( 44 final @Nullable MediaType contentType, final ByteString content) { 45 return new RequestBody() { 46 @Override public @Nullable MediaType contentType() { 47 //请求体需要的内容类型 48 return contentType; 49 } 50 51 @Override public long contentLength() throws IOException { 52 //写入BufferedSink 的长度 53 return content.size(); 54 } 55 56 @Override public void writeTo(BufferedSink sink) throws IOException { 57 //将需要传输的字节,写入缓存BufferedSink 中 58 sink.write(content); 59 } 60 }; 61 } 62 63 /** 【创建的请求体3】Returns a new request body that transmits {@code content}. */ 64 public static RequestBody create(final @Nullable MediaType contentType, final byte[] content) { 65 return create(contentType, content, 0, content.length); 66 } 67 68 /**【创建的请求体4】 Returns a new request body that transmits {@code content}. */ 69 public static RequestBody create(final @Nullable MediaType contentType, final byte[] content, 70 final int offset, final int byteCount) { 71 if (content == null) throw new NullPointerException("content == null"); 72 Util.checkOffsetAndCount(content.length, offset, byteCount); 73 return new RequestBody() { 74 @Override public @Nullable MediaType contentType() { 75 return contentType; 76 } 77 78 @Override public long contentLength() { 79 return byteCount; 80 } 81 82 @Override public void writeTo(BufferedSink sink) throws IOException { 83 sink.write(content, offset, byteCount); 84 } 85 }; 86 } 87 88 /** Returns a new request body that transmits the content of {@code file}. */ 89 //【创建的请求体5】创建一个请求体,传输文件file内容,其实就是file写入bufferedSink 90 public static RequestBody create(final @Nullable MediaType contentType, final File file) { 91 if (file == null) throw new NullPointerException("content == null"); 92 93 return new RequestBody() { 94 @Override public @Nullable MediaType contentType() { 95 return contentType; 96 } 97 98 @Override public long contentLength() { 99 return file.length(); 100 } 101 102 @Override public void writeTo(BufferedSink sink) throws IOException { 103 Source source = null; 104 try { 105 //文件写入BufferedSink 106 source = Okio.source(file); 107 sink.writeAll(source); 108 } finally { 109 Util.closeQuietly(source); 110 } 111 } 112 }; 113 } 114 }
【MultipartBody】
1. MultipartBody本质一个是一个RequestBody,具有自己的contentType+BufferedSink,是POST请求的最外层封装,需要添加多个Part;
2. Part对象组成:Headers+RequestBody。是MultipartBody的成员变量,需要写入MultipartBody的BufferedSink中。
Http请求中Content-Type
客户端在进行http请求服务器的时候,需要告诉服务器请求的类型,服务器在返回给客户端的数据的时候,也需要告诉客户端返回数据的类型默认的ContentType为text/html 也就是网页格式.
常用的内容类型 text/plain :纯文本格式 .txt text/xml : XML格式 .xml image/gif :gif图片格式 .gif image/jpeg :jpg图片格式 .jpg image/png:png图片格式 .png audio/mp3 : 音频mp3格式 .mp3 audio/rn-mpeg :音频mpga格式 .mpga video/mpeg4 : 视频mp4格式 .mp4 video/x-mpg : 视频mpa格式 .mpg video/x-mpeg :视频mpeg格式 .mpeg video/mpg : 视频mpg格式 .mpg
以application开头的媒体格式类型: application/xhtml+xml :XHTML格式 application/xml : XML数据格式 application/atom+xml :Atom XML聚合格式 application/json : JSON数据格式 application/pdf :pdf格式 application/msword : Word文档格式 application/octet-stream : 二进制流数据(如常见的文件下载)
RequestBody的数据格式都要指定Content-Type,常见的有三种
application/x-www-form-urlencoded 数据是个普通表单
multipart/form-data 数据里有文件
application/json 数据是个json
普通表单并没有指定Content-Type,这是因为FormBody继承了RequestBody,它已经指定了数据类型为application/x-www-form-urlencoded。
[普通表单]
RequestBody body = new FormBody.Builder() .add("键", "值") .add("键", "值") ... .build();
[json表单 ]
1 MediaType JSON = MediaType.parse("application/json; charset=utf-8"); 2 RequestBody body = RequestBody.create(JSON, "你的json");
[数据包含文件 ]
1 RequestBody requestBody = new MultipartBody.Builder() 2 .setType(MultipartBody.FORM) 3 .addFormDataPart("file", file.getName(), RequestBody.create(MediaType.parse("image/png") 4 .build()
MultipartBody也是继承了RequestBody,看下源码可知它适用于这五种ContentType:
public static final MediaType MIXED = MediaType.parse("multipart/mixed"); public static final MediaType ALTERNATIVE = MediaType.parse("multipart/alternative"); public static final MediaType DIGEST = MediaType.parse("multipart/digest"); public static final MediaType PARALLEL = MediaType.parse("multipart/parallel"); public static final MediaType FORM = MediaType.parse("multipart/form-data");
其他的媒体类型支持参考下面网址中列出的MIME类型:http://www.w3school.com.cn/media/media_mimeref.asp
3.2 单个文件上传
【简单示例】将sd卡的文件读取,然后构造fileBody,使用post请求上传。
1 /** 2 * [3]post http文件上传 3 * 最主要的还是创建合适的RequestBody 4 */ 5 private void okHttpPostFile(String url) { 6 7 OkHttpClient okHttpClient = new OkHttpClient(); 8 9 //读取外部的sd卡的文件上传; 10 File file = new File(Environment.getExternalStorageDirectory(), "mimi.mp4"); 11 RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream"), 12 file); 13 14 Request request = new Request.Builder() 15 .url(url) 16 .post(fileBody) 17 .build(); 18 19 Call call = okHttpClient.newCall(request); 20 21 //[1]同步请求; 22 /* try { 23 call.execute(); 24 } catch (IOException e) { 25 e.printStackTrace(); 26 }*/ 27 28 //[2]异步请求 29 call.enqueue(new Callback() { 30 @Override 31 public void onFailure(Call call, IOException e) { 32 33 } 34 35 @Override 36 public void onResponse(Call call, Response response) throws IOException { 37 38 } 39 }); 40 }
3.3 多个文件的上传
【重点】RequestBody create(MediaType contentType, final File file)构造文件请求体RequestBody ,并且添加到MultiPartBody中
1 /** 2 * [多个文件的上传] 3 */ 4 5 private void postMultiFile(String url){ 6 7 HashMap<String, String> urlHeaders = new HashMap<>(); 8 9 OkHttpClient client = new OkHttpClient(); 10 11 // form 表单形式上传,MultipartBody的内容类型是表单格式,multipart/form-data 12 MultipartBody.Builder multiPartBodyBuilder= new MultipartBody 13 .Builder() 14 .setType(MultipartBody.FORM); 15 16 //参数 17 HashMap<String,String> params = new HashMap<>(); 18 if (params != null) { 19 for (String key : params.keySet()) { 20 if (params.get(key)!=null){ 21 //增加一系列的参数; 22 multiPartBodyBuilder.addFormDataPart(key, params.get(key)); 23 } 24 } 25 } 26 27 //需要上传的文件,需要携带上传的文件(小型文件 不建议超过500K) 28 HashMap<String,String> files= new HashMap<>(); 29 if (files != null) { 30 for (String key : files.keySet()) { 31 //重点:RequestBody create(MediaType contentType, final File file)构造文件请求体RequestBody 32 RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-data"), files.get(key)); 33 multiPartBodyBuilder.addFormDataPart(key, files.get(key).getClass().getName(),requestBody); 34 } 35 } 36 37 //构造请求request 38 Request request = new Request.Builder() 39 .headers(urlHeaders == null ? new Headers.Builder().build() : Headers.of(urlHeaders)) 40 .url(url) 41 .post(multiPartBodyBuilder.build()) 42 .build(); 43 44 //异步执行请求 45 client.newCall(request).enqueue(new Callback() { 46 @Override 47 public void onFailure(Call call, IOException e) { 48 49 } 50 51 @Override 52 public void onResponse(Call call, Response response) throws IOException { 53 //非主线程 54 if (response.isSuccessful()) { 55 String str = response.body().string(); 56 Log.i("tk", response.message() + " , body " + str); 57 58 } else { 59 Log.i("tk" ,response.message() + " error : body " + response.body().string()); 60 } 61 } 62 }); 63 }
6.post json 请求
1 /** 2 * [5]post json 请求; 3 */ 4 private void okHttpPostJSon(String url, String json) throws IOException { 5 6 OkHttpClient okHttpClient = new OkHttpClient(); 7 8 MediaType JSON = MediaType.parse("application/json; charset=utf-8"); 9 RequestBody requestBody = RequestBody.create(JSON, json); 10 11 Request request = new Request.Builder() 12 .url(url) 13 .post(requestBody) 14 .build(); 15 16 Call call = okHttpClient.newCall(request); 17 //[1]同步请求; 18 call.execute(); 19 //[2]异步请求; 20 call.enqueue(new Callback() { 21 @Override 22 public void onFailure(Call call, IOException e) { 23 24 } 25 26 @Override 27 public void onResponse(Call call, Response response) throws IOException { 28 29 } 30 }); 31 32 }
7.使用Gson来解析JSON响应
1 /** 2 * [6]使用Gson来解析JSON响应 3 * <p> 4 * Gson是一个在JSON和Java对象之间转换非常方便的api。这里我们用Gson来解析Github API的JSON响应。 5 * 注意: ResponseBody.charStream() 使用响应头 Content-Type 指定的字符集来解析响应体。默认是UTF-8。
* 添加依赖: compile 'com.google.code.gson:gson:2.6.2' 6 */ 7 private void ParseGson(String url) { 8 OkHttpClient okHttpClient = new OkHttpClient(); 9 10 final Gson gson = new Gson(); 11 12 //默认是get请求; 13 final Request request = new Request.Builder() 14 .url("https://api.github.com/gists/c2a7c39532239ff261be") 15 .build(); 16 17 Call call = okHttpClient.newCall(request); 18 19 call.enqueue(new Callback() { 20 @Override 21 public void onFailure(Call call, IOException e) { 22 23 } 24 25 @Override 26 public void onResponse(Call call, Response response) throws IOException { 27 if (!response.isSuccessful()) 28 throw new IOException("Unexpected code " + response); 29 Gist gist = gson.fromJson(response.body().charStream(), Gist.class); 30 for (Map.Entry<String, GistFile> entry : gist.files.entrySet()) { 31 System.out.println(entry.getKey()); 32 System.out.println(entry.getValue().content); 33 } 34 } 35 }); 38 } 39 40 private static class Gist { 41 Map<String, GistFile> files; 42 } 43 44 private static class GistFile { 45 String content; 46 }
8.get请求文件下载
1 /** 2 * [6] get请求文件下载 3 * 此处的实例是下载图片 4 */ 5 private void okHttpGetDownLoadFile(String url) { 6 OkHttpClient okHttpClient = new OkHttpClient(); 7 final Request request = new Request.Builder() 8 .get() 9 .url(url) 10 .build(); 11 12 Call call = okHttpClient.newCall(request); 13 call.enqueue(new Callback() { 14 @Override 15 public void onFailure(Call call, IOException e) { 16 17 } 18 19 @Override 20 public void onResponse(Call call, Response response) throws IOException { 21 //拿到图片的字节流,然后保存为了本地的一张图片 22 if (response.isSuccessful()) { 23 //获取数据流; 24 InputStream is = response.body().byteStream(); 25 26 //读到文件中; 27 int len = 0; 28 File file = new File(Environment.getExternalStorageDirectory(), "downLoadFile" + 29 ".png"); 30 FileOutputStream fos = new FileOutputStream(file); 31 32 byte[] buf = new byte[1024]; 33 while ((len = is.read(buf)) != -1) { 34 fos.write(buf, 0, len); 35 } 36 fos.flush(); 37 fos.close(); 38 is.close(); 39 } 40 41 42 /* 直接将图片设置给了ImagView; 43 final Bitmap bitmap = BitmapFactory.decodeStream(is); 44 runOnUiThread(new Runnable() { 45 @Override 46 public void run() { 47 imageView.setImageBitmap(bitmap); 48 } 49 }); 50 is.close();*/ 51 } 52 }); 53 54 55 }
9.给文件的下载加上进度条
1 /** 2 * [7] 给文件的下载加上进度条 3 * 进度的获取是在回调函数onResponse()中去获取 4 * (1)使用response.body().contentLength()拿到文件总大小 5 * (2)在while循环中每次递增我们读取的buf的长度 6 */ 7 8 private void okHttpDownLoadWithProgress(String url) { 9 OkHttpClient okHttpClient = new OkHttpClient(); 10 final Request request = new Request.Builder() 11 .get() 12 .url(url) 13 .build(); 14 15 Call call = okHttpClient.newCall(request); 16 call.enqueue(new Callback() { 17 @Override 18 public void onFailure(Call call, IOException e) { 19 20 } 21 22 @Override 23 public void onResponse(Call call, Response response) throws IOException { 24 //拿到图片的字节流,然后保存为了本地的一张图片 25 if (response.isSuccessful()) { 26 //获取数据流; 27 InputStream is = response.body().byteStream(); 28 29 //每次下载的实时更新的进度; 30 long sum = 0; 31 //下载的文件总长度; 32 final long total = response.body().contentLength(); 33 34 //读到文件中; 35 int len = 0; 36 File file = new File(Environment.getExternalStorageDirectory(), "downLoadFile" + 37 ".png"); 38 FileOutputStream fos = new FileOutputStream(file); 39 40 byte[] buf = new byte[1024]; 41 while ((len = is.read(buf)) != -1) { 42 fos.write(buf, 0, len); 43 44 sum += len; 45 final long finalSum = sum; 46 Log.d("okHttp", "下载进度:" + finalSum + "/" + total); 47 48 //将进度设置到主线程显示 49 /* runOnUiThread(new Runnable() { 50 @Override 51 public void run() { 52 //将进度设置到TextView中 53 contentTv.setText(finalSum + "/" + total); 54 } 55 });*/ 56 57 } 58 fos.flush(); 59 fos.close(); 60 is.close(); 61 } 62 } 63 64 }); 65 66 }
【另外一种写法】
1 /** 2 * @param url 下载连接 3 * @param saveDir 储存下载文件的SDCard目录 4 * @param params url携带参数 5 * @param extraHeaders 请求携带其他的要求的headers 6 * @param listener 下载监听 7 */ 8 public void download(final String url, final String saveDir, 9 HashMap<String,String> params, HashMap<String,String> extraHeaders, 10 final OnDownloadListener listener) { 11 12 OkHttpClient okHttpClient = new OkHttpClient(); 13 //构造请求Url 14 HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder(); 15 if (params != null) { 16 for (String key : params.keySet()) { 17 if (params.get(key)!=null){ 18 urlBuilder.setQueryParameter(key, params.get(key));//非必须 19 } 20 } 21 } 22 //构造请求request 23 Request request = new Request.Builder() 24 .url(urlBuilder.build()) 25 .headers(extraHeaders == null ? new Headers.Builder().build() : Headers.of(extraHeaders))//headers非必须 26 .get() 27 .build(); 28 29 //异步执行请求 30 okHttpClient.newCall(request).enqueue(new Callback() { 31 @Override 32 public void onFailure(Call call, IOException e) { 33 // 下载失败 34 listener.onDownloadFailed(); 35 } 36 @Override 37 public void onResponse(Call call, Response response) throws IOException { 38 //非主线程 39 InputStream is = null; 40 byte[] buf = new byte[2048]; 41 int len = 0; 42 FileOutputStream fos = null; 43 // 储存下载文件的目录 44 String savePath = isExistDir(saveDir); 45 try { 46 //获取响应的字节流 47 is = response.body().byteStream(); 48 //文件的总大小 49 long total = response.body().contentLength(); 50 File file = new File(savePath); 51 fos = new FileOutputStream(file); 52 long sum = 0; 53 //循环读取输入流 54 while ((len = is.read(buf)) != -1) { 55 fos.write(buf, 0, len); 56 sum += len; 57 int progress = (int) (sum * 1.0f / total * 100); 58 // 下载中 59 if(listener != null){ 60 listener.onDownloading(progress); 61 } 62 63 } 64 fos.flush(); 65 // 下载完成 66 if(listener != null){ 67 listener.onDownloadSuccess(); 68 } 69 70 } catch (Exception e) { 71 if(listener != null){ 72 listener.onDownloadFailed(); 73 } 74 75 } finally { 76 try { 77 if (is != null) 78 is.close(); 79 } catch (IOException e) { 80 } 81 try { 82 if (fos != null) 83 fos.close(); 84 } catch (IOException e) { 85 } 86 } 87 } 88 }); 89 }
10.文件的上传增加进度
1 /** 2 * [8]文件的上传增加进度 3 * 对于上传的进度的处理会比较麻烦,因为具体的上传过程是在RequestBody中由OkHttp帮我们处理上传, 4 * 而且OkHttp并没有给我们提供上传进度的接口,这里做法是自定义类继承RequestBody, 5 * 然后重写其中的方法,将其中的上传进度通过接口回调暴露出来供使用 6 * 详见:包下的progressRequestBody类; 7 * 在原有的RequestBody上包装了一层,最后在我们的使用中在post()方法中传入我们的CountingRequestBody对象即可 8 * <p> 9 * 最主要的还是创建合适的RequestBody 10 */ 11 private void okHttpPostFileWithProgress(String url) { 12 13 OkHttpClient okHttpClient = new OkHttpClient(); 14 15 //读取外部的sd卡的文件上传; 16 File file = new File(Environment.getExternalStorageDirectory(), "mimi.mp4"); 17 // RequestBody fileBody = RequestBody.create(MediaType.parse 18 // ("application/octet-stream"),file); 19 20 //*****此处使用的RequestBody是经过修改; 21 RequestBody fileBody = progressRequestBody.create(MediaType.parse 22 ("application/octet-stream"), file); 23 24 Request request = new Request.Builder() 25 .url(url) 26 .post(fileBody) 27 .build(); 28 29 Call call = okHttpClient.newCall(request); 30 31 //[1]同步请求; 32 /* try { 33 call.execute(); 34 } catch (IOException e) { 35 e.printStackTrace(); 36 }*/ 37 38 //[2]异步请求 39 call.enqueue(new Callback() { 40 @Override 41 public void onFailure(Call call, IOException e) { 42 43 } 44 45 @Override 46 public void onResponse(Call call, Response response) throws IOException { 47 48 } 49 }); 50 }
【progressRequestBody类的实现】
1 package com.oztaking.www.a02_okhttp.okHttpUtils; 2 3 import java.io.IOException; 4 5 import okhttp3.MediaType; 6 import okhttp3.RequestBody; 7 import okhttp3.ResponseBody; 8 import okio.Buffer; 9 import okio.BufferedSink; 10 import okio.ForwardingSink; 11 import okio.Okio; 12 import okio.Sink; 13 14 15 /** 16 * [8]文件的上传增加进度 17 * 对于上传的进度的处理会比较麻烦,因为具体的上传过程是在RequestBody中由OkHttp帮我们处理上传, 18 * 而且OkHttp并没有给我们提供上传进度的接口,这里做法是自定义类继承RequestBody, 19 * 然后重写其中的方法,将其中的上传进度通过接口回调暴露出来供使用 20 * 实质:在原有的RequestBody上包装了一层,最后在我们的使用中在post()方法中传入我们的CountingRequestBody对象即可 21 */ 22 23 public class progressRequestBody extends RequestBody { 24 25 //实际要使用的requestBody 26 private ResponseBody mDelegate; 27 //回调监听 28 private okHttpUpLoadProgressListener mListener; 29 private CountingSink countingSink; 30 31 public progressRequestBody(ResponseBody mDelegate, okHttpUpLoadProgressListener mListener) { 32 this.mDelegate = mDelegate; 33 this.mListener = mListener; 34 } 35 36 @Override 37 public MediaType contentType() { 38 return mDelegate.contentType(); 39 } 40 41 /* 返回文件总的字节大小 42 * 如果文件大小获取失败则返回-1*/ 43 @Override 44 public long contentLength() { 45 return mDelegate.contentLength(); 46 } 47 48 @Override 49 public void writeTo(BufferedSink sink) throws IOException { 50 countingSink = new CountingSink(sink); 51 //将CountingSink转化为BufferedSink供writeTo()使用 52 BufferedSink bufferedSink = Okio.buffer(countingSink); 53 bufferedSink.flush(); 54 } 55 56 57 protected final class CountingSink extends ForwardingSink { 58 59 private long byteWritten; 60 61 public CountingSink(Sink delegate) { 62 super(delegate); 63 } 64 65 /** 66 * ** 67 * 上传时调用该方法,在其中调用回调函数将上传进度暴露出去, 68 * 该方法提供了缓冲区的自己大小 69 * @param source 70 * @param byteCount 71 * @throws IOException 72 */ 73 74 @Override 75 public void write(Buffer source, long byteCount) throws IOException { 76 super.write(source, byteCount); 77 byteWritten += byteCount; 78 mListener.onRequestProgress(byteWritten,contentLength()); 79 } 80 } 81 82 83 public static interface okHttpUpLoadProgressListener { 84 /** 85 * 暴露出上传进度 86 * 87 * @param byteWritten 已经上传的字节大小 88 * @param contentLength 文件的总字节大小 89 */ 90 void onRequestProgress(long byteWritten, long contentLength); 91 } 92 }
11.同步get
1 /** 2 * [9] 同步get 3 * 下载一个文件,打印他的响应头,以string形式打印响应体。 4 * 响应体的 string() 方法对于小文档来说十分方便、高效。但是如果响应体太大(超过1MB),应避免适应 5 * string() 方法 ,因为他会将把整个文档加载到内存中。 6 * 对于超过1MB的响应body,应使用流的方式来处理body。 7 */ 8 private void okHttpGetSyncHeader(String url) throws IOException { 9 10 OkHttpClient okHttpClient = new OkHttpClient(); 11 Request request = new Request.Builder() 12 .url(url) 13 .build(); 14 15 Call call = okHttpClient.newCall(request); 16 //同步请求; 17 Response response = call.execute(); 18 if (!response.isSuccessful()) { 19 throw new IOException("response error" + response); 20 } 21 //获取到response的头部; 22 Headers responseHeaders = response.headers(); 23 int len = responseHeaders.size(); 24 for (int i = 0; i < len; i++) { 25 //response的头部的每个信息 26 System.out.println(responseHeaders.name(i) + ":" + responseHeaders.value(i)); 27 } 28 //打印头部的所有信息; 29 System.out.println(response.body().string()); 30 }
12.异步get
1 /** 2 * [10]异步get 3 * 4 * @param url 5 */ 6 private void okHttpGetASyncHeader(String url) { 7 OkHttpClient okHttpClient = new OkHttpClient(); 8 Request request = new Request.Builder() 9 .url(url) 10 .get() 11 .build(); 12 Call call = okHttpClient.newCall(request); 13 call.enqueue(new Callback() { 14 @Override 15 public void onFailure(Call call, IOException e) { 16 17 } 18 19 @Override 20 public void onResponse(Call call, Response response) throws IOException { 21 if (!response.isSuccessful()) { 22 throw new IOException("Unexpected code " + response); 23 } 24 //获取到response的头部; 25 Headers headers = response.headers(); 26 int len = headers.size(); 27 for (int i = 0; i < len; i++) { 28 //response的头部的每个信息 29 System.out.println("responseHeaders:" + headers.name(i) + ":" + headers.value 30 (i)); 31 } 32 //打印头部的所有信息; 33 System.out.println(response.body().string()); 34 } 35 }); 36 }
13.提取响应头
1 /** 2 * [11] 提取响应头 3 * 典型的HTTP头 像是一个 Map<String, String> :每个字段都有一个或没有值。 4 * 但是一些头允许多个值,像Guava的Multimap。 5 * 例如:HTTP响应里面提供的 Vary 响应头,就是多值的。OkHttp的api试图让这些情况都适用。 6 * 【header(name, value)】 可以设置唯一的name、value。 7 * 如果已经有值,旧的将被移除,然后添加新的。 8 * 【addHeader(name, value)】 可以添加多值(添加,不移除已有的)。 9 * 当读取响应头时,使用 header(name) 返回最后出现的name、value。 10 * 通常情况这也是唯一的name、value。 11 * 如果没有值,那么 header(name) 将返回null。 12 * 如果想读取字段对应的所有值,使用 headers(name) 会返回一个list(如上例)。 13 * 为了获取所有的Header,Headers类支持按index访问。 14 */ 15 16 private void addHeaders(String url) { 17 OkHttpClient okHttpClient = new OkHttpClient(); 18 19 Request request = new Request.Builder() 20 .url(url) 21 .header("User-Agent", "OkHttp Headers.java") 22 .addHeader("Accept", "application/json:q=0.5") 23 .addHeader("Accept", "application/vnd.github.v3+json") 24 .build(); 25 26 Call call = okHttpClient.newCall(request); 27 call.enqueue(new Callback() { 28 @Override 29 public void onFailure(Call call, IOException e) { 30 31 } 32 33 @Override 34 public void onResponse(Call call, Response response) throws IOException { 35 System.out.println(response.header("Server")); 36 System.out.println(response.header("Date")); 37 System.out.println(response.headers("Vary")); 38 } 39 }); 40 }
14.post 提交String
1 /** 2 * [11]post 提交String 3 * 提交了一个markdown文档到web服务,以HTML方式渲染markdown。 4 * 因为整个请求体都在内存中,因此避免使用此api提交大文档(大于1MB)。 5 */ 6 7 private void okHttpPostString(String url) { 8 MediaType mediaType = MediaType.parse("text/x-markdown; charset=utf-8"); 9 OkHttpClient okHttpClient = new OkHttpClient(); 10 String postBody = "" 11 + "Releases\n" 12 + "--------\n" 13 + "\n" 14 + " * _1.0_ May 6, 2013\n" 15 + " * _1.1_ June 15, 2013\n" 16 + " * _1.2_ August 11, 2013\n"; 17 18 //创建请求体 19 RequestBody requestBody = RequestBody.create(mediaType, postBody); 20 21 Request request = new Request.Builder() 22 .url(url) 23 .post(requestBody) 24 .build(); 25 26 Call call = okHttpClient.newCall(request); 27 call.enqueue(new Callback() { 28 @Override 29 public void onFailure(Call call, IOException e) { 30 31 } 32 33 @Override 34 public void onResponse(Call call, Response response) throws IOException { 35 if (!response.isSuccessful()) 36 throw new IOException("Unexpected code " + response); 37 System.out.println(response.body().string()); 38 } 39 }); 40 41 }
15.post 方式提交流
1 /** 2 * [12]post 方式提交流 3 * <p> 4 * 以流的方式POST提交请求体。请求体的内容由流写入产生。 5 * 这个例子是流直接写入Okio的BufferedSink。 6 * 你的程序可能会使用 OutputStream ,你可以使用 BufferedSink.outputStream()来获取 7 */ 8 9 private void okHttpPostStream(String url) { 10 11 final MediaType mediaType = MediaType.parse("text/x-markdown; charset=utf-8"); 12 13 OkHttpClient okHttpClient = new OkHttpClient(); 14 15 RequestBody requestBody = new RequestBody() { 16 @Override 17 public MediaType contentType() { 18 return mediaType; 19 } 20 21 @Override 22 public void writeTo(BufferedSink sink) throws IOException { 23 /** 24 * 关键的方法:sink.writeUtf8 25 */ 26 sink.writeUtf8("Numbers\n"); 27 for (int i = 2; i <= 997; i++) { 28 sink.writeUtf8(String.format(" * %s = %s\n", i, factor(i))); 29 } 30 } 31 32 private String factor(int n) { 33 for (int i = 2; i < n; i++) { 34 int x = n / i; 35 if (x * i == n) return factor(x) + " × " + i; 36 } 37 return Integer.toString(n); 38 } 39 }; 40 41 Request request = new Request.Builder() 42 .url(url) 43 .post(requestBody) 44 .build(); 45 Call call = okHttpClient.newCall(request); 46 call.enqueue(new Callback() { 47 @Override 48 public void onFailure(Call call, IOException e) { 49 50 } 51 52 @Override 53 public void onResponse(Call call, Response response) throws IOException { 54 if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); 55 System.out.println(response.body().string()); 56 } 57 }); 58 }
16.响应缓存
1 /** 2 * [13]响应缓存----没有弄明白; 3 * 【注意】Okhttp仅支持get请求缓存,其他请求方式需要自己实现 4 * 5 * 缓存响应:需要建立可以读写的缓存目录和限定缓存大小。 6 * 建立的缓存目录应该是私有的,不信任的程序应不能读取缓存内容。 7 * 一个缓存目录不能同时拥有多个缓存访问。 8 * 大多数程序只需要调用一次 new OkHttp() ,在第一次调用时配置好缓存,然后其他使用到的地方只需要调用这个实例。 9 * 否则两个缓存示例互相干扰,破坏响应缓存,而且有可能会导致程序崩溃。 10 * 响应缓存使用HTTP头作为配置。 11 * 你可以在请求头中添加 Cache-Control: max-stale=3600 ,OkHttp缓存会支持。 12 * 你的服务通过响应头确定响应缓存多长时间,例如使用 Cache-Control: max-age=9600 13 */ 14 15 private void okHttCacheResponse(File cacheDirector) { 16 17 //使用OkHttp的Cache,首先需要指定缓存的路径和大小 18 int cacheSize = 10 * 1024 * 1024; 19 Cache cache = new Cache(cacheDirector, cacheSize); 20 21 OkHttpClient okHttpClient 22 = new OkHttpClient() 23 .newBuilder() 24 .cache(cache) 25 .build(); 26 27 Request request = new Request.Builder() 28 .url("http://publicobject.com/helloworld.txt") 29 .build(); 30 31 Call call1 = okHttpClient.newCall(request); 32 call1.enqueue(new Callback() { 33 @Override 34 public void onFailure(Call call, IOException e) { 35 36 } 37 38 @Override 39 public void onResponse(Call call, Response response1) throws IOException { 40 if (!response1.isSuccessful()) 41 throw new IOException("Unexpected code " + response1); 42 43 String s = response1.body().string(); 44 Logger.d("response1:"+s); 45 Logger.d("response1 Cache response:"+response1.cacheResponse()); 46 Logger.d("response1 network response:"+response1.networkResponse()); 47 } 48 }); 49 50 Call call2 = okHttpClient.newCall(request); 51 call2.enqueue(new Callback() { 52 @Override 53 public void onFailure(Call call, IOException e) { 54 55 } 56 57 @Override 58 public void onResponse(Call call, Response response2) throws IOException { 59 if (!response2.isSuccessful()) 60 throw new IOException("Unexpected code " + response2); 61 62 String s = response2.body().string(); 63 Logger.d("response2:"+s); 64 Logger.d("response2 Cache response:"+response2.cacheResponse()); 65 Logger.d("response2 network response:"+response2.networkResponse()); 66 } 67 }); 68 69 // System.out.println("Response 2 equals Response 1? " + 70 // response2.equals(response1)); 71 }
17.取消一个Call
1 /** 2 * [14]取消一个Call--没有弄明白; 3 * 4 * 使用 Call.cancel() 可以立即停止掉一个正在执行的call。 5 * 如果一个线程正在写请求或者读响应,将会引发IOException 。 6 * 当call没有必要的时候,使用这个api可以节约网络资源。 7 * 例如当用户离开一个应用时。不管同步还是异步的call都可以取消。 8 * 你可以通过tags来同时取消多个请求。 9 * 当你构建一请求时,使用 RequestBuilder.tag(tag) 来分配一个标签。 10 * 之后你就可以用 OkHttpClient.cancel(tag) 来取消所有带有这个tag的call 11 */ 12 private void okHttpCancelCall(){ 13 14 ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); 15 OkHttpClient okHttpClient = new OkHttpClient(); 16 17 Request request = new Request.Builder() 18 .url("http://httpbin.org/delay/2")// This URL is served with a 2 second delay. 19 .tag(1) 20 .build(); 21 22 final long nanoTime = System.nanoTime(); 23 final Call call = okHttpClient.newCall(request); 24 25 // Schedule a job to cancel the call in 1 second. 26 executor.schedule(new Runnable() { 27 @Override 28 public void run() { 29 System.out.printf("%.2f Canceling +++++ call.%n",(System.nanoTime() - nanoTime) / 1e9f); 30 call.cancel(); 31 System.out.printf("%.2f Canceled ------call.%n", (System.nanoTime() - nanoTime) / 1e9f); 32 } 33 },1, TimeUnit.SECONDS); 34 35 System.out.printf("%.2f Canceling #### call.%n",(System.nanoTime() - nanoTime) / 1e9f); 36 call.enqueue(new Callback() { 37 @Override 38 public void onFailure(Call call, IOException e) { 39 40 } 41 42 @Override 43 public void onResponse(Call call, Response response) throws IOException { 44 System.out.printf("%.2f Call was $$$$$ bexpected to fail, but completed: %s%n", 45 (System.nanoTime() - nanoTime) / 1e9f, response); 46 } 47 }); 48 49 50 }
18.设置超时时间
1 /** 2 * [15]超时 3 * 没有响应时使用超时结束call。 4 * 没有响应的原因可能是客户点链接问题、服务器可用性问题或者这之间的其他东西。 5 * OkHttp支持连接,读取和写入超时。 6 */ 7 8 private void okHttpTimeout(){ 9 OkHttpClient okHttpClient = new OkHttpClient().newBuilder() 10 .connectTimeout(1,TimeUnit.SECONDS) 11 .readTimeout(1,TimeUnit.SECONDS) 12 .writeTimeout(1,TimeUnit.SECONDS) 13 .pingInterval(10,TimeUnit.SECONDS) //websocket 轮训间隔 14 .build(); 15 16 Request request = new Request.Builder() 17 .url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay. 18 .build(); 19 20 Call call = okHttpClient.newCall(request); 21 call.enqueue(new Callback() { 22 @Override 23 public void onFailure(Call call, IOException e) { 24 25 } 26 27 @Override 28 public void onResponse(Call call, Response response) throws IOException { 29 System.out.println("Response completed: " + response.body().string()); 30 } 31 }); 32 }
19.配置call
1 /** 2 * [16]每个call的配置--clone 3 * 使用 OkHttpClient ,所有的HTTP Client配置包括代理设置、超时设置、缓存设置。当你需要为单个call改变 4 * 配置的时候,clone 一个 OkHttpClient 。这个api将会返回一个浅拷贝(shallow copy),你可以用来单独自 5 * 定义。下面的例子中,我们让一个请求是500ms的超时、另一个是3000ms的超时。 6 */ 7 8 9 /* public void run() throws Exception { 10 final OkHttpClient client = new OkHttpClient(); 11 Request request = new Request.Builder() 12 .url("http://httpbin.org/delay/1") // This URL is served with a 1 second delay. 13 .build(); 14 try { 15 Response response = client.clone() // Clone to make a customized OkHttp for this 16 request..setReadTimeout(500, TimeUnit.MILLISECONDS) 17 .newCall(request) 18 .execute(); 19 System.out.println("Response 1 succeeded: " + response); 20 } catch (IOException e) { 21 System.out.println("Response 1 failed: " + e); 22 } 23 try { 24 Response response = client.clone() // Clone to make a customized OkHttp for this 25 request..setReadTimeout(3000, TimeUnit.MILLISECONDS) 26 .newCall(request) 27 .execute(); 28 System.out.println("Response 2 succeeded: " + response); 29 } catch (IOException e) { 30 System.out.println("Response 2 failed: " + e); 31 } 32 }*/ 33 34 35 private void OkHttpCallConfigure(){ 36 OkHttpClient okHttpClient = new OkHttpClient(); 37 Request request = new Request.Builder() 38 .url("http://httpbin.org/delay/1") // This URL is served with a 1 second delay 39 .build(); 40 41 Call call = okHttpClient.newCall(request); 42 Call clone = call.clone(); 43 44 }
20.Cookie 保存
1 /** 2 * [17]Cookie 保存 3 * Request经常都要携带Cookie,上面说过request创建时可以通过header设置参数,Cookie也是参数之一 4 * 然后可以从返回的response里得到新的Cookie,可能得想办法把Cookie保存起来。 5 * 但是OkHttp可以不用我们管理Cookie,自动携带,保存和更新Cookie。 6 * 方法是在创建OkHttpClient设置管理Cookie的CookieJar 7 * 8 */ 9 private void OkHttpCookieSave(String url){ 10 11 final HashMap<String, List<Cookie>> cookieStore = new HashMap<>(); 12 13 OkHttpClient okHttpClient = new OkHttpClient().newBuilder() 14 .cookieJar(new CookieJar() { 15 @Override 16 public void saveFromResponse(HttpUrl url, List<Cookie> cookies) { 17 // 保存cookie通常使用SharedPreferences 18 cookieStore.put(url.host(), cookies); 19 } 20 21 @Override 22 public List<Cookie> loadForRequest(HttpUrl url) { 23 // 从保存位置读取,注意此处不能为空,否则会导致空指针 24 List<Cookie> cookies = cookieStore.get(url.host()); 25 return cookies != null ? cookies : new ArrayList<Cookie>(); 26 27 } 28 }) 29 .build(); 30 31 Request request = new Request.Builder() 32 .url(url) 33 .header("Cookie", "xxx") //添加cookie; 34 .build(); 35 }
21.添加拦截器
1 /** 2 * [21]添加拦截器 3 * okHttp3的实现使用的是链式的拦截器,同时也开放了自定义拦截器接口 4 */ 6 private void okHttpAddInterceptor(){ 7 new OkHttpClient.Builder() 8 // 此种拦截器将会在请求开始的时候调用 9 .addInterceptor(new Interceptor() { 10 @Override 11 public Response intercept(Chain chain) throws IOException { 12 return null; 13 } 14 }) 15 // 连接成功,读写数据前调用 16 .addNetworkInterceptor(new Interceptor() { 17 @Override 18 public Response intercept(Chain chain) throws IOException { 19 return null; 20 } 21 }).build(); 22 }
22.https的支持
1 /** 2 * [22]https的支持添加 3 * okhttp3完全支持https,设置证书即可 4 * 未完成,报错; 5 */ 6 private void okHttpHttps(){ 7 OkHttpClient okHttpClient = new OkHttpClient.Builder() 8 .sslSocketFactory(SSLSocketFactory(), X509TrustManager) 9 .build(); 10 }
23.Websocket支持
1 /** 2 * [23]Websocket支持 3 * okhttp3支持websocket,简易推送,轮训都可以使用 4 * websocket协议首先会发起http请求,握手成功后,转换协议保持长连接,类似心跳 5 * 6 * 【说明】创建一个OkHttpClient,通常是单例, 7 * 如果要自定义一些属性那就要用内部的Builder来构造 8 * 9 * [参考文章]https://blog.csdn.net/xlh1191860939/article/details/75452342/ 10 */ 11 12 private void okHttpWebSocket(String url){ 13 OkHttpClient okHttpClient = new OkHttpClient.Builder().build(); 14 Request request = new Request.Builder() 15 .url(url) 16 .build(); 17 okHttpClient.newWebSocket(request, new WebSocketListener() { 18 @Override 19 public void onOpen(WebSocket webSocket, Response response) { 20 super.onOpen(webSocket, response); 21 // 连接成功 22 } 23 24 @Override 25 public void onMessage(WebSocket webSocket, String text) { 26 super.onMessage(webSocket, text); 27 // 当收到文本消息 28 } 29 30 @Override 31 public void onMessage(WebSocket webSocket, ByteString bytes) { 32 super.onMessage(webSocket, bytes); 33 // 收到字节消息,可转换为文本 34 } 35 36 @Override 37 public void onClosing(WebSocket webSocket, int code, String reason) { 38 super.onClosing(webSocket, code, reason); 39 40 } 41 42 @Override 43 public void onClosed(WebSocket webSocket, int code, String reason) { 44 super.onClosed(webSocket, code, reason); 45 // 连接被关闭 46 } 47 48 @Override 49 public void onFailure(WebSocket webSocket, Throwable t, Response response) { 50 super.onFailure(webSocket, t, response); 51 // 连接失败 52 } 53 }); 54 }
24.混淆
1 在混淆文件(proguard-rules.pro)中添加如下语句: 2 -dontwarn okhttp3.**
【参考文章】
【1】https://www.jianshu.com/p/9aa969dd1b4d
【2】http://www.cnblogs.com/ct2011/p/3997368.html
【3】https://www.jianshu.com/p/1873287eed87
1 /** 2 * [3]post http文件上传 3 * 最主要的还是创建合适的RequestBody 4 */ 5 private void okHttpPostFile(String url) { 6 7 OkHttpClient okHttpClient = new OkHttpClient(); 8 9 //读取外部的sd卡的文件上传; 10 File file = new File(Environment.getExternalStorageDirectory(), "mimi.mp4");
//关键是requestBody的构造 11 RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream"),file); 13 14 Request request = new Request.Builder() 15 .url(url) 16 .post(fileBody) 17 .build(); 18 19 Call call = okHttpClient.newCall(request); 20 21 //[1]同步请求; 22 /* try { 23 call.execute(); 24 } catch (IOException e) { 25 e.printStackTrace(); 26 }*/ 27 28 //[2]异步请求 29 call.enqueue(new Callback() { 30 @Override 31 public void onFailure(Call call, IOException e) { 32 33 } 34 35 @Override 36 public void onResponse(Call call, Response response) throws IOException { 37 38 } 39 }); 40 }
5.Post方式提交分块请求
1 /** 2 * [4-2] Post方式提交分块请求 3 * MultipartBuilder 可以构建复杂的请求体,与HTML文件上传形式兼容。 4 * 多块请求体中每块请求都是一个请求体,可以定义自己的请求头。 5 * 这些请求头可以用来描述这块请求,例如他的 Content-Disposition 。 6 * 如果 Content-Length 和 Content-Type 可用的话,他们会被自动添加到请求头中 7 * 8 * @param url 9 * @throws Exception 【注意】上面的描述是基于okhttp2.0;代码是基于okhttp3.0; 10 */ 11 private void okHttpPostBlockData(String url) throws Exception { 12 13 final String IMGUR_CLIENT_ID = "..."; 14 final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png"); 15 OkHttpClient client = new OkHttpClient(); 16 17 MultipartBody multipartBody = new MultipartBody.Builder() 18 .setType((MultipartBody.FORM)) 19 .addPart(Headers.of("Content-Disposition", "form-data; name=\"title\""), 20 RequestBody.create(null, "Square Logo")) 21 .addPart(Headers.of("Content-Disposition", "form-data; name=\"image\""), 22 RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square" + 23 ".png"))) 24 .build(); 25 26 Request request = new Request.Builder() 27 .header("Authorization", "Client-ID " + IMGUR_CLIENT_ID) 28 .url("") 29 .post(multipartBody) 30 .build(); 31 32 Response response = client.newCall(request).execute(); 33 if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); 34 System.out.println(response.body().string()); 35 }
浙公网安备 33010602011771号