一、介绍
OkHttp是一个第三方类库,用于android中请求网络。这是一个开源项目,是安卓端最火热的轻量级框架,由移动支付Square公司贡献。用于替代HttpUrlConnection和Apache HttpClient,HttpUrlConnection使用较复杂,android API23 里已移除HttpClient。OkHttp 处理了很多网络疑难杂症:会从很多常用的连接问题中自动恢复。如果您的服务器配置了多个IP地址,当第一个IP连接失败的时候,OkHttp会自动尝试下一个IP。OkHttp还处理了代理服务器问题和SSL握手失败问题。
二、创建OkHttpClient
public OkHttpUtil() {
// okhttp报文打印拦截器
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
Log.i(TAG, "----http报文:" + message);
}
});
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
okHttpClient = new OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.build();
}
OkHttpUtil是单例模式,全局中新建一个OkHttpClient的实例就可以了。
1、设置日志打印拦截器
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
Log.i(TAG, "----http报文:" + message);
}
});
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
2、设置超时
没有响应时使用超时结束call。没有响应的原因可能是客户点连接问题、服务器可用性问题或者这之间的其他东西。OkHttp支持连接,读取和写入超时。
okHttpClient = new OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.connectTimeout(10, TimeUnit.SECONDS) // 连接超时
.readTimeout(10, TimeUnit.SECONDS) // 读超时
.writeTimeout(10, TimeUnit.SECONDS) // 写超时
.build();
三、Get请求
1、同步请求
public String get(String url) {
Request.Builder builder = new Request.Builder();
Request request = builder.get() // 默认是get方式,不调用这个也一样
.url(url)
.build();
try {
Response response = okHttpClient.newCall(request).execute();
if (!response.isSuccessful()) {
return null;
}
return response.body().string();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
1)、发送一个get请求的步骤,首先构造一个Request对象,参数最起码有个url,当然你可以通过Request.Builder设置更多的参数比如:header、method等。
2)、然后通过request的对象去构造得到一个Call对象,类似于将你的请求封装成了任务,既然是任务,就会有execute()和cancel()等方法。
3)、最后我们希望以同步的方式去执行请求,所以我们调用的是call.execute(),这时程序会阻塞,网络返回后继续向下执行。
网络请求成功会返回一个Response对象,通过response.isSuccessful()可以判断返回码是否是成功,如果是成功通过response.body().string()获取返回的body数据。
2、异步请求
public void get(String url, final ResponseListener listener) {
Request.Builder builder = new Request.Builder();
Request request = builder.get() // 默认是get方式,不调用这个也一样
.url(url)
.build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
listener.onFailure(e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
listener.onSucess(response.body().string());
} else {
listener.onFailure("response code is err." + response.code());
}
}
});
}
ResponseListener是自定义的响应数据Listener
public interface ResponseListener {
void onSucess(String res);
void onFailure(String msg);
}
前两步和同步请求一样,第三步时调用call.execute()异步请求数据,请求成功回调Callback.onResponse,请求失败回调Callback.onFailure。异步请求不会阻塞程序。
四、Post请求
Post的同步请求、异步请求和Get一样,所以后面不讨论异步请求,所以交易都使用同步请求。
Post使用步骤比Get多一步新建RequestBody,其他都是一样,RequestBody中包含Http报文的body部分。
1、Post提交json数据
public String post(String url, String json) {
MediaType mediaType = MediaType.parse("application/json; charset=utf-8");
RequestBody requestBody = RequestBody.create(mediaType, json);
Request.Builder builder = new Request.Builder();
Request request = builder.url(url)
.post(requestBody)
.build();
try {
Response response = okHttpClient.newCall(request).execute();
if (!response.isSuccessful()) {
return null;
}
return response.body().string();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
使用MediaType指定Content-type,"application/json; charset=utf-8"表示请求数据是json,字符编码是utf-8。
后台如何使用Content-type?Spring MVC 使用@RequestMapping 注解的consumes指定处理请求的提交内容类型(Content-Type),例如application/json, text/html,收窄请求范围,如果用户发送的请求内容类型不匹配则方法不会响应请求。使用如下:
public class HomeController {
// 请求内容类型Content-type必须为text/html
@RequestMapping(value = "/action8",consumes="text/html")
public String action8(Model model) {
model.addAttribute("message", "请求的提交内容类型(Content-Type)是text/html");
return "foo/index";
}
}
2、Post提交form表单
public String postForm(String url, Map<String, String> form) {
FormBody.Builder formBuilder = new FormBody.Builder();
Iterator iterator = form.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry entry = (Map.Entry) iterator.next();
formBuilder.add((String) entry.getKey(), (String) entry.getValue());
}
RequestBody requestBody = formBuilder.build();
Request.Builder reqBuilder = new Request.Builder();
Request request = reqBuilder.url(url)
.post(requestBody)
.build();
try {
Response response = okHttpClient.newCall(request).execute();
if (!response.isSuccessful()) {
return null;
}
return response.body().string();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
使用FormBody创建Form表单请求的body。FormBody会自动加上content-type:application/x-www-form-urlencoded。
3、Post方式提交流
public String postStream(String url) {
RequestBody requestBody = new RequestBody() {
@Override
public MediaType contentType() {
return MediaType.parse("text/x-markdown; charset=utf-8");
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
sink.writeString("post流方式提交数据", Charset.forName("GBK"));
}
};
Request.Builder builder = new Request.Builder();
Request request = builder.url(url)
.post(requestBody)
.build();
try {
Response response = okHttpClient.newCall(request).execute();
if (response.isSuccessful()) {
return response.body().string();
} else {
return null;
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
这里其实是自己实现RequestBody,写入到Http body中的数据在writeTo()方法写入。上面2中的FormBody实际也是继承RequestBody,实现writeTo()方法完成body数据的组建。实现contentType()放回content-type。
4、Post方式提交文件
public String postFile(String url, File file) {
RequestBody requestBody = RequestBody.create(MediaType.parse("\"text/x-markdown; charset=utf-8\""), file);
Request.Builder builder = new Request.Builder();
Request request = builder.url(url)
.post(requestBody)
.build();
try {
Response response = okHttpClient.newCall(request).execute();
if (response.isSuccessful()) {
return response.body().string();
} else {
return null;
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
RequestBody.create(MediaType.parse("\"text/x-markdown; charset=utf-8\""), file);使用RequestBody.create传入文件File对象就可以创建body。
5、Post提交分块请求
public String postMultiPart(String url, File imgFile) {
MultipartBody.Builder bodyBuilder = new MultipartBody.Builder();
MultipartBody body = bodyBuilder.addPart(Headers.of("Content-Disposition", "form-data; name=\"title\""), RequestBody.create(null, "title"))
.addPart(Headers.of("Content-Disposition", "form-data; name=\"image\""), RequestBody.create(MediaType.parse("image/png"), imgFile))
.build();
Request.Builder builder = new Request.Builder();
Request request = builder.url(url)
.post(body)
.build();
try {
Response response = okHttpClient.newCall(request).execute();
if (response.isSuccessful()) {
return response.body().string();
} else {
return null;
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
MultipartBuilder可以构建复杂的请求体,与HTML文件上传形式兼容。多块请求体中每块请求都是一个请求体,可以定义自己的请求头。这些请求头可以用来描述这块请求,例如他的Content-Disposition。如果Content-Length和Content-Type可用的话,他们会被自动添加到请求头中。MultipartBuilder的addPart()方法可以添加一个请求体。
五、取消一个请求Call
1、知道需要取消的Call对象使用:call.cancel();取消
2、通过Tag取消Call
创建Request时指定Tag
Request request = builder.url(url)
.get()
.tag("tag") // 指定tag
.build();
遍历当前OkHttpClient所有Request的tag,根据tag取消指定的Call
public static void cancelTag(OkHttpClient client, Object tag) {
if (client == null || tag == null) return;
for (Call call : client.dispatcher().queuedCalls()) {
if (tag.equals(call.request().tag())) {
call.cancel();
}
}
for (Call call : client.dispatcher().runningCalls()) {
if (tag.equals(call.request().tag())) {
call.cancel();
}
}
}
3、取消所有Call
OkHttpClient.dispatcher().cancelAll();