这是我参与「第四届青训营」笔记创作活动的第1天。安卓基础班的课程还没有开始,在查看了大作业的要求后,决定先学习一下自己还不熟悉的技术,今天就决定从OkHttp开始学习。
准备工作
Gradle 依赖导入
implementation("com.squareup.okhttp3:okhttp:4.9.0")
清单文件权限设置
使用网络服务需要在清单文件中添加网络权限
<uses-permission android:name="android.permission.INTERNET"/>
OkHttp的同步与异步请求
发送get同步请求
OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder().url("https://www.httpbin.org/get?a=1&b=2").build();
//准备好请求的call对象
Call call = okHttpClient.newCall(request);
try {
Response response = call.execute();
} catch (IOException e) {
e.printStackTrace();
}
1.new OkHttpClient()构建一个OkHttpClient对象
2.使用Request.Builder()构建一个request对象
3.调用okHttpClient的newCall()方法传入刚才创建的request对象,创建一个准备好请求的call对象
4.执行call.execute()方法发送请求,同时获得返回的response。
注意:在使用同步请求时,需要开辟一个子线程来执行操作,get请求的参数列表直接声明在了网址中。
发送get异步请求
Request request = new Request.Builder().url("https://www.httpbin.org/get?a=1&b=2").build();
Call call = okHttpClient.newCall(request);
//异步请求
call.enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
//请求失败回调
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
//请求结束回调,并不意味着通信成功,只意味着与服务器通信成功
if (response.isSuccessful()) {
Log.i(TAG, "getAsync: " + response.body().string());
}
}
});
这里与get同步请求不同的是,异步请求调用的是call对象的enqueue()方法,而切异步请求是不需要开辟一个子线程的,因为enqueue()方法内会开辟子线程。
CallBack()中有两个重写的方法。在请求失败以后回调onFailure()方法。在请求结束后回调onResponse()方法,而onResponse()被调用也不意味着通信成功,只是与服务器通信成功(无论访问码是200、404或者其他)。
发送post同步请求
FormBody formBody = new FormBody.Builder().add("a", "1").add("a", "2").build();
//指明用post请求,否则自动使用get,post需要传递请求体
Request request = new Request.Builder().url("https://www.httpbin.org/post").post(formBody).build();
//准备好请求的call对象
Call call = okHttpClient.newCall(request);
try {
Response response = call.execute();
Log.i(TAG, "postSync: " + response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
发送post请求,需要在构建Request对象时指明使用post,否则会自动使用get,此时Request.Builder().post()的post()中需要传入一个请求体作为参数,这里使用的FormBody类构建的formBody传入,其中添加了一系列参数。
注意:post请求的参数列表不在网址中。
发送post异步请求
//使用FormBody提交的方式构建请求体
FormBody formBody = new FormBody.Builder().add("a", "1").add("a", "2").build();
//指明用post请求,否则自动使用get,post需要传递请求体
Request request = new Request.Builder().url("https://www.httpbin.org/post").post(formBody).build();
//准备好请求的call对象
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
}
});
post异步请求也是使用Call对象的enqueue()方法,同样的要构建请求体,其他部分同上。
post请求的数据提交方式
HTTP协议规定了post提交的数据必须放在请求体中,但并没有规定必须使用的编码方式,常用的数据编码方式可以在以下网址查看: www.runoob.com/http/http-c…
几个例子:
- Content-Type:application/x-www-form-urlencoded : 数据编码为名/值对,默认类型
- Content-Type:multipart/form-data : 数据编码为一条消息,一般用于文件上传
- Content-Type:application/json : 用于提交json数据
File file = new File("/Users/lidayu/Downloads/te1.txt");
MultipartBody multipartBody = new MultipartBody.Builder()
.addFormDataPart("file1", file.getName(), RequestBody.create(file, MediaType.parse("text/plain")))
.build();
Request request = new Request.Builder().url("https://www.httpbin.org/post").post(multipartBody).build();
使用MultipartBody.Builder()构建请求体,addFormDataPart()传入要上传的文件信息,在RequestBody.create()方法的MediaType.parse()中声明数据编码方式。
OkHttp构建者的设置
OkHttp的自定义配置:
OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
okHttp可以在构建时设置拦截器,拦截器有以下两种
OkHttpClient okHttpClient = new OkHttpClient.Builder().addInterceptor(new xxx).build();
OkHttpClient okHttpClient = new OkHttpClient.Builder().addNetWorkInterceptor(new xxx).build();
加入拦截器后,执行请求时,请求过程中会执行一次拦截器的拦截方法,接到拦截器的响应之后,当前请求还没有发给服务器,可通过chain对象得到request对象,对其进行前置与后置处理:
OkHttpClient okHttpClient = new OkHttpClient.Builder().cache(new Cache(new File("/Users/lidayu/Downloads"),1024*1024)).addInterceptor(new Interceptor() {
@NonNull
@Override
public Response intercept(@NonNull Chain chain) throws IOException {
//前置处理
Request request = chain.request()
.newBuilder().addHeader("os", "android").addHeader("version", "1.0")
.build();
Response response = chain.proceed(request);
//后置处理
return response;
}
}).addNetworkInterceptor(new Interceptor() {
@NonNull
@Override
public Response intercept(@NonNull Chain chain) throws IOException {
System.out.println(chain.request().header("version"));
return chain.proceed(chain.request());
}
}).build();
在执行前置处理后,请求才会发送给服务器。当多个请求需要添加上同样的数据内容时,使用拦截器会非常方便。
注意:在定义了多个同种拦截器时,先定义的先执行,当两种拦截器都被定定义时,addInterceptor(new xxx)在addNetWorkInterceptor(new xxx)之前执行。
小结
1.发起get请求时,Call对象的execute()方法发起的是同步请求,需要在子线程中使用。enqueue()方法发起的是异步请求,不需要开辟子线程。
2.发起post请求时,需要在构建Request对象时指明使用post请求,并传入请求体,请求体有多种选择,根据需求使用即可,比如:FormBody,MultipartBody等。
3.可以灵活的使用拦截器减少重复工作。