从Android4.4开始HttpURLConnection的底层实现采用OkHttp
同步请求:发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式。同步通信方式要求通信双方以相同的时钟频率进行,而且准确协调,通过共享一个单个时钟或定时脉冲源保证发送方和接收方的准确同步,效率较高。
异步请求:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式。异步通信方式不要求双方同步,收发方可采用各自的时钟源,双方遵循异步的通信协议,以字符为数据传输单位,发送方传送字符的时间间隔不确定,发送效率比同步传送效率低。
测试URL: https://www.httpbin.org/
添加依赖
1
| implementation("com.squareup.okhttp3:okhttp:4.9.0")
|
相关权限
1 2
| <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
同步请求
get请求
创建一个OkHttpClient对象作为请求器
1
| OkHttpClient okHttpClient = new OkHttpClient();
|
创建http请求对象、设置请求地址、得到Request的实例对象、把request实例对象交给请求器、调用call的execute方法实现同步请求
1 2 3 4 5 6 7 8 9 10
|
Request.Builder builder = new Request.Builder();
Request request = builder.url("https://www.httpbin.org/get").build();
Call call = okHttpClient.newCall(request);
Response response = call.execute();
|
Response报错,需要进行异常捕获处理
1 2 3 4 5 6
| try {
Response response = call.execute(); } catch (IOException e) { e.printStackTrace(); }
|
执行如下代码,会闪退并报错,因为安卓中完成网络请求必须请求一个子线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Request.Builder builder = new Request.Builder();
Request request = builder.url("https://www.httpbin.org/get").build();
Call call = okHttpClient.newCall(request); try {
Response response = call.execute();
String str = response.body().string();
code.setText(str); } catch (IOException e) { e.printStackTrace(); }
|
创建一个子线程
1 2 3 4 5 6 7
| new Thread(){ @Override public void run() { super.run(); } }.start();
|
在线程中完成get请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| new Thread(){ @Override
public void run() {
Request.Builder builder = new Request.Builder();
Request request = builder.url("https://www.httpbin.org/get").build();
Call call = okHttpClient.newCall(request); try {
Response response = call.execute();
String str = response.body().string();
runOnUiThread(new Runnable() { @Override public void run() {
code.setText(str); } }); } catch (IOException e) { e.printStackTrace(); } } }.start();
|
post请求
同步post请求与同步get请求几乎一样,多了post数据而已
1 2 3 4
| FormBody formBody = new FormBody.Builder().add("a","1").add("b","2").build();
Request request1 = new Request.Builder().url("https://www.lxx6.com").post(formBody).build();
|
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| new Thread(){ @Override public void run() {
FormBody formBody = new FormBody.Builder().add("a","1").add("b","2").build();
Request request1 = new Request.Builder().url("https://www.lxx6.com").post(formBody).build();
Call call1 = okHttpClient.newCall(request1); try { Response response = call1.execute(); String str = response.body().string(); runOnUiThread(new Runnable() { @Override public void run() { code.setText(str); } }); } catch (IOException e) { e.printStackTrace(); } } }.start();
|
异步请求
get请求
创建一个OkHttpClient对象作为请求器
1
| OkHttpClient okHttpClient = new OkHttpClient();
|
创建请求对象
1 2 3 4
| Request request = new Request.Builder().url("https://blog.xxin.xyz").build();
Call call = okHttpClient.newCall(request);
|
这里call调用enqueue()方法,不再是execute()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
call.enqueue(new Callback() { @Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
runOnUiThread(new Runnable() { @Override public void run() { code.setText("请求失败"); } }); }
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
if (response.isSuccessful()){
String str = response.body().string();
runOnUiThread(new Runnable() { @Override public void run() { code.setText(str); } }); } } });
|
注意:
- 异步请求不会阻塞后续代码的执行。
- 异步请求不需要创建新线程,创建enqueue时已经创建了新的线程
post请求
异步post与异步get几乎相同,唯多了post数据而已
1 2 3 4
| FormBody formBody = new FormBody.Builder().add("a","1").add("b","2").build();
Request request1 = new Request.Builder().url("https://www.amyxiaoxin.top").post(formBody).build();
|
这里call调用enqueue()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| Call call1 = okHttpClient.newCall(request1); call1.enqueue(new Callback() { @Override
public void onFailure(@NotNull Call call, @NotNull IOException e) { runOnUiThread(new Runnable() { @Override public void run() { code.setText("请求失败"); } }); } @Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { if (response.isSuccessful()){ String str = response.body().string(); runOnUiThread(new Runnable() { @Override public void run() { code.setText(str); } }); } } });
|
post上传文件
协议规定POST提交的数据必须放在请求体中,但协议并没有规定数据必须使用什么编码方式
常用的编码方式有
- Content-Type: application/x-www-form-urlencoded;数据被编码为“名称/键值对”,默认类型(FormBody就是这种类型)
- Content-Type: multipart/from-date;数据编码为一条消息,一般用于文件上传
- Content-Type: application/octet-stream;提交二进制数据,如果用于文件上传,只能上传一个文件
- Content-Type: application/json;提交json数据
数据编码方式对照表
多文件上传
用multipart/from-date编码方式实现多文件上传
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| new Thread(){ @Override public void run() {
String File1Path = "/storage/emulated/0/MT2/apks/temp/file1.txt"; String File2Path = "/storage/emulated/0/MT2/apks/temp/file2.txt";
File file1 = new File(File1Path); File file2 = new File(File2Path);
RequestBody requestBody1 = RequestBody.create(file1, MediaType.parse("text/plain")); RequestBody requestBody2 = RequestBody.create(file2, MediaType.parse("text/plain"));
MultipartBody multipartBody = new MultipartBody.Builder() .addFormDataPart("file1",file1.getName(),requestBody1) .addFormDataPart("file2",file2.getName(),requestBody2) .build();
Request request = new Request.Builder().url("https://www.httpbin.org/post").post(multipartBody).build();
Call call = okHttpClient.newCall(request); try { Response execute = call.execute(); String str = execute.body().string(); runOnUiThread(new Runnable() { @Override public void run() { code.setText(str); } }); } catch (IOException e) { e.printStackTrace(); } } }.start();
|
二进制流上传文件
不知道文件类型,使用application/octet-stream二进制流上传文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| new Thread(){ @Override public void run() {
String FilePath = "/storage/emulated/0/MT2/apks/temp/img.png";
File file = new File(FilePath);
RequestBody requestBody = RequestBody.create(file,MediaType.parse("application/x-png"));
Request request = new Request.Builder().url("https://www.httpbin.org/post").post(requestBody).build(); Call call = okHttpClient.newCall(request); try { Response execute = call.execute(); String str = execute.body().string(); runOnUiThread(new Runnable() { @Override public void run() { code.setText(str); } }); } catch (IOException e) { e.printStackTrace(); } } }.start();
|
提交json数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| new Thread(){ @Override public void run() {
String json = "{\"a\":2,\"b\":2}";
RequestBody requestBody = RequestBody.create(json,MediaType.parse("application/json"));
Request request = new Request.Builder().url("https://www.httpbin.org/post").post(requestBody).build();
Call call = okHttpClient.newCall(request); try { Response execute = call.execute(); String str = execute.body().string(); runOnUiThread(new Runnable() { @Override public void run() { code.setText(str); } }); } catch (IOException e) { e.printStackTrace(); } } }.start();
|
get下载文件
OkHttp简单的文件下载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| Request request = new Request.Builder().url("https://www.httpbin.org/image/png").build(); okHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { runOnUiThread(new Runnable() { @Override public void run() { code.setText("下载失败"); } }); }
@Override public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { if (response.isSuccessful()) {
InputStream inputStream = response.body().byteStream();
FileOutputStream fileOutputStream = null;
String filePath = "/storage/emulated/0/MT2/apks/temp/download.png"; File file = new File(filePath);
fileOutputStream = new FileOutputStream(file); byte[] bytes = new byte[1024]; int len = 0;
long fileSize = response.body().contentLength(); long sum = 0; while ((len = inputStream.read(bytes)) != -1) { fileOutputStream.write(bytes,0,len);
sum += len; Log.d(TAG, "进度: " + (int) ((sum * 1.0f / fileSize) * 100) + "%"); } } } });
|
拦截器
addInterceptor(应用拦截器,先执行):
- 不需要担心中间过程的响应,如重定向和重试
- 总是只调用一次,即使HTTP响应是从缓存中获取
- 观察应用程序的初衷. 不关心OkHttp注入的头信息如: If-None-Match
- 允许短路而不调用 Chain.proceed(),即中止调用
- 允许重试,使 Chain.proceed()调用多
addNetworkInterceptor(网络拦截器,后执行):
- 能够操作中间过程的响应,如重定向和重试
- 当网络短路而返回缓存响应时不被调用
- 只观察在网络上传输的数据
- 携带请求来访问连接
addInterceptor(应用拦截器)
可以添加很多个,执行顺序与添加顺序相同(但总会比addNetworkInterceptor先执行)
在请求器发送请求前会执行一次Interceptor拦截器的intercept方法,intercept方法内可以通过chain请求对象request,对请求对象进行发送请求前置操作,例如添加响应头等,然后通过chain.proceed(request)获得到处理之后的最终的响应信息并返回出去,这样就省下了在拦截器外重复添加响应头的繁琐
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| OkHttpClient okHttpClient = new OkHttpClient.Builder() .addInterceptor(new Interceptor() { @NotNull @Override
public Response intercept(@NotNull Chain chain) throws IOException {
Request request = chain.request();
Request request1 = request.newBuilder() .addHeader("os","andrroid") .addHeader("version","1.0") .build();
Response response = chain.proceed(request1); return response; } }) .build();
new Thread(){ @Override public void run() { Request request = new Request.Builder().url("https://www.httpbin.org/get").build(); Call call = okHttpClient.newCall(request); try { Response response = call.execute(); String str = response.body().string(); Log.d(TAG, "addInterceptor: " + str); runOnUiThread(new Runnable() { @Override public void run() { code.setText(str); } }); } catch (IOException e) { e.printStackTrace(); } } }.start();
|
可见应用拦截器添加的响应头
addNetworkInterceptor(网络拦截器)
addNetworkInterceptor总会比addInterceptor后执行,所以它可以读取addNetworkInterceptor中添加的信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| OkHttpClient okHttpClient = new OkHttpClient.Builder() .addNetworkInterceptor(new Interceptor() { @NotNull @Override public Response intercept(@NotNull Chain chain) throws IOException {
Request request = chain.request();
Request request1 = request.newBuilder() .addHeader("os","android") .addHeader("version","1.0") .build(); Response response = chain.proceed(request1); return response; } }) .build();
new Thread(){ @Override public void run() { Request request = new Request.Builder().url("https://www.httpbin.org/get").build(); Call call = okHttpClient.newCall(request); try { Response response = call.execute(); String str = response.body().string(); Log.d(TAG, "addInterceptor: " + str); runOnUiThread(new Runnable() { @Override public void run() { code.setText(str); } }); } catch (IOException e) { e.printStackTrace(); } } }.start();
|
OkHttp的缓存
添加缓存后okHttpClient会自动判断是否可以使用缓存并且自动保存缓存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| OkHttpClient okHttpClient = new OkHttpClient.Builder()
.cache(new Cache(new File("/storage/emulated/0/MT2/apks/temp/temp"),1024*1024)) .build();
new Thread(){ @Override public void run() { Request request = new Request.Builder().url("https://www.httpbin.org/get").build(); Call call = okHttpClient.newCall(request); try { Response response = call.execute(); String str = response.body().string(); Log.d(TAG, "addInterceptor: " + str); runOnUiThread(new Runnable() { @Override public void run() { code.setText(str); } }); } catch (IOException e) { e.printStackTrace(); } } }.start();
|
拓展:try/catch异常抛出与捕获
1 2 3 4 5 6 7 8 9
| try {
} catch(异常类名A e){
} finally {
}
|