OkHttp官网地址
OkHttp GitHub地址
官网中有很详细的Java doc文档,我们可以很方便的查到OkHttp里面各个API的功能和使用方法。
get方式获取数据
private String doGet(String areaId) {
//1.创建一个okClient对象
OkHttpClient client = new OkHttpClient();
//2.创建一个Request请求
String url = String.format(URL_Weather,areaId);
Request request = new Request.Builder().url(url).build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d(TAG,"请求服务器失败");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
// 注:该回调是子线程,非主线程
Log.d(TAG,"请求服务器成功:");
weatherResult = response.body().string();
}
});
return weatherResult;
}
以上是OkHttp的异步get请求,是okhttp3异步方法,不需要我们开启工作线程执行网络请求,返回的结果也在工作线程中;
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
return response.body().string();
} else {
throw new IOException("Unexpected code " + response);
}
以上是同步方法,如果使用同步get方法,需要自行开辟子线程进行网络请求,此处不推荐~
post方式获取数据
OkHttp的post方法可以提交键值对、文本、Stream、file等形式的数据,然后使用Request的post方法来提交请求体RequestBody。
private String doPost(String areaId) {
new Thread(new Runnable() {
@Override
public void run() {
try {
// 请求完整url:http://api.k780.com:88/?app=weather.future&weaid=101281001&&appkey=%s&sign=%s&format=json
String url = "http://api.k780.com:88/";
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.connectTimeout(800, TimeUnit.MILLISECONDS)
.cache(new Cache(getExternalCacheDir().getAbsoluteFile(),10*1024*1024))
.writeTimeout(5, TimeUnit.SECONDS);
OkHttpClient okHttpClient = builder.build();
RequestBody formBody = new FormBody.Builder().add("app", "weather.future")
.add("weaid", "101281001").add("appkey", APPKEY).add("sign",
SIGN).add("format", "json")
.build();
Request request = new Request.Builder().url(url).post(formBody).build();
okhttp3.Response response = okHttpClient.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
weatherResult = response.body().string();
Log.i(TAG, weatherResult);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
return weatherResult;
}
以下代码参考自OkHttp官方示例:Http官方教程
post提交流:
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse("text/x-markdown; charset=utf-8");
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
RequestBody requestBody = new RequestBody() {
@Override public MediaType contentType() {
return MEDIA_TYPE_MARKDOWN;
}
@Override public void writeTo(BufferedSink sink) throws IOException {
sink.writeUtf8("Numbers\n");
sink.writeUtf8("-------\n");
for (int i = 2; i <= 997; i++) {
sink.writeUtf8(String.format(" * %s = %s\n", i, factor(i)));
}
}
private String factor(int n) {
for (int i = 2; i < n; i++) {
int x = n / i;
if (x * i == n) return factor(x) + " × " + i;
}
return Integer.toString(n);
}
};
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(requestBody)
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
post提交文件:
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse("text/x-markdown; charset=utf-8");
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
File file = new File("README.md");
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
post提交表单(键值对):
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
RequestBody formBody = new FormBody.Builder()
.add("search", "Jurassic Park")
.build();
Request request = new Request.Builder()
.url("https://en.wikipedia.org/w/index.php")
.post(formBody)
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
此处需注意:
okHttp2.7用的是new FormEncodingBuilder(),而OkHttp3.x用的是FormBody.Builder();
Post提交String
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse("text/x-markdown; charset=utf-8");
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
String postBody = ""
+ "Releases\n"
+ "--------\n"
+ "\n"
+ " * _1.0_ May 6, 2013\n"
+ " * _1.1_ June 15, 2013\n"
+ " * _1.2_ August 11, 2013\n";
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody))
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
Post提交分块请求
MultipartBuilder可以构建复杂的请求体,与HTML文件上传形式兼容。多块请求体中每块请求都是一个请求体,可以定义自己的请求头。这些请求头可以用来描述这块请求,例如他的Content-Disposition。如果Content-Length和Content-Type可用的话,他们会被自动添加到请求头中。
private static final String IMGUR_CLIENT_ID = "...";
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
// Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image
RequestBody requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM)
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"title\""),
RequestBody.create(null, "Square Logo"))
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"image\""),
RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png")))
.build();
Request request = new Request.Builder()
.header("Authorization", "Client-ID " + IMGUR_CLIENT_ID)
.url("https://api.imgur.com/3/image")
.post(requestBody)
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful())
throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
此处OkHttp3也有和之前版本不一样的地方,OkHttp3使用了MultipartBody.Builder()的方法来创建RequestBody,而之前是使用MultipartBuilder()。
更多OkHttp的改动详见:okhttp3与旧版本okhttp的区别分析
关于MediaType
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse("text/x-markdown; charset=utf-8");
text/html : HTML格式
text/plain :纯文本格式
text/xml : XML格式
image/gif :gif图片格式
image/jpeg :jpg图片格式
image/png:png图片格式
以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 : 二进制流数据(如常见的文件下载)
application/x-www-form-urlencoded : 中默认的encType,form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式)
另外一种常见的媒体格式是上传文件之时使用的:
multipart/form-data : 需要在表单中进行文件上传时,就需要使用该格式
注意:MediaType.parse(“image/png”)里的”image/png”不知道该填什么,可以参考—》http://www.w3school.com.cn/media/media_mimeref.asp
如何使用呢?(在请求体里面写入类型和需要写入的数据,通过post请求)
String body = “hdsoifhjoihdsfh”;
RequestBody body = RequestBody.create(MEDIA_TYPE_MARKDOWN, body);
设置缓存目录和大小:
响应缓存使用HTTP头作为配置。你可以在请求头中添加Cache-Control: max-stale=3600 ,OkHttp缓存会支持。你的服务通过响应头确定响应缓存多长时间,例如使用Cache-Control: max-age=9600。
public CacheResponse(File cacheDirectory) throws Exception {
int cacheSize = 10 * 1024 * 1024; // 10 MiB
Cache cache = new Cache(cacheDirectory, cacheSize);
client = new OkHttpClient();
client.setCache(cache);
}
设置超时时间和缓存
和OkHttp2.x有区别的是不能通过OkHttpClient直接设置超时时间和缓存了,而是通过OkHttpClient.Builder来设置,通过builder配置好OkHttpClient后用builder.build()来返回OkHttpClient,所以我们通常不会调用new OkHttpClient()来得到OkHttpClient,而是通过builder.build():
File sdcache = getExternalCacheDir();
int cacheSize = 10 * 1024 * 1024;
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.cache(new Cache(sdcache.getAbsoluteFile(), cacheSize));
OkHttpClient mOkHttpClient=builder.build();
代码示例:
private String doGet(String areaId) {
String url = String.format(URL_Weather, areaId);
Request request = new Request.Builder().url(url).build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d(TAG, "请求服务器失败");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d(TAG, "请求服务器成功:\n"+response.body().string());
}
});
return weatherResult;
}
private String doPost(String areaId) {
try {
// 请求完整url:http://api.k780.com:88/?app=weather.future&weaid=101281001&&appkey=10003&sign=b59bc3ef6191eb9f747dd4e83c99f2a4&format=json
String url = "http://api.k780.com:88/";
RequestBody formBody = new FormBody.Builder().add("app", "weather.future")
.add("weaid", areaId).add("appkey", "10003").add("sign",
"b59bc3ef6191eb9f747dd4e83c99f2a4").add("format", "json")
.build();
Request request = new Request.Builder().url(url).post(formBody).build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
weatherResult = response.body().string();
Log.i(TAG, weatherResult);
}
});
} catch (Exception e) {
e.printStackTrace();
}
return weatherResult;
}
private void doUpload() {
File file = new File("/sdcard/zoky.txt");
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
.build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d(TAG,"上传失败");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.i(TAG, "上传成功:\n" + response.body().string());
}
});
}
private void doDownload() {
String url = "http://avatar.csdn.net/5/8/5/1_zoky_ze.jpg";
Request request = new Request.Builder().url(url).build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d(TAG,"下载失败");
}
@Override
public void onResponse(Call call, Response response) {
int len;
InputStream inputStream = response.body().byteStream();
FileOutputStream fileOutputStream;
try {
fileOutputStream = new FileOutputStream(new File("/sdcard/zoky.jpg"));
byte[] buffer = new byte[2048];
while ((len = inputStream.read(buffer)) != -1) {
fileOutputStream.write(buffer, 0, len);
}
fileOutputStream.flush();
Log.d(TAG, "文件下载成功"+ response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
内部调用流程
有上图可以看出,OkHttp不管是异步调用还是同步调用,最终都是通过getResponseWithInterceptorChain()方法来进行网络请求
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}
在getResponseWithInterceptorChain()方法中加了一些拦截器,然后启动一个拦截器调用链,拦截器递归调用之后最后返回请求的响应Response。这里的拦截器分层的思想就是借鉴的网络里的分层模型的思想。请求从最上面一层到最下一层,响应从最下一层到最上一层,每一层只负责自己的任务,对请求或响应做各自的操作。OkHttp还支持用户自定义拦截器,插入到最顶层和CallServerInterceptor上一层的位置。square官方就有一个Logging Interceptor,用于打印网络请求日志的拦截器。
下图是拦截器的调用流程及各自的执行的操作。
RealInterceptorChain的proceed(),每次重新创建一个RealInterceptorChain对象,然后调用下一层的拦截器的interceptor.intercept()方法。
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// 1、该拦截器在Request阶段负责的事情
// 2、调用RealInterceptorChain.proceed(),其实是递归调用下一层拦截器的intercept方法
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
//3、该拦截器在Response阶段负责的事情,然后返回到上一层拦截器的 response阶段
return response;
}
}
参考文章:
OkHttp使用进阶 译自OkHttp Github官方教程
OkHttp3用法全解析
okhttp3与旧版本okhttp的区别分析
Android OkHttp完全解析 是时候来了解OkHttp了
OkHTT分析