第1章 OkHttp的介绍
1.1 OkHttp的简介
现在Android中的网络请求框架,基本都是OkHttp和Retrofit一统天下,而Retrofit又是以okhttp为基础,所以系统学习okhttp的使用和原理就很有必要了。
OkHttp是由Square公司开发的一个处理网络请求的开源项目,是目前Android使用中公认最好用、最广泛的网络框架,在接口封装上做的简单易用。从Android4.4开始HttpURLConnection的底层实现采用的是OkHttp。
OkHttp地址:square/okhttp (github.com)
它有以下默认特性:
- 支持HTTP/2,允许所有同一个主机地址的请求共享同一个socket连接
- 使用连接池减少请求延时
- 透明的GZIP压缩减少响应数据的大小
- 缓存响应内容,避免一些完全重复的请求
当网络出现问题的时候OkHttp 会自动恢复一般的连接问题,如果你的服务有多个IP地址,当第一个IP请求失败时,OkHttp会交替尝试你配置的其他IP。
测试Http请求的URL:https://www.httpbin.org/
1.2 引入OkHttp
1.2.1 引入依赖
在build.gradle文件的dependencies中添加以下代码:
implementation "com.squareup.okhttp3:okhttp:4.9.3"
1.2.2 权限声明
在AndroidMainFest.xml文件中添加网络请求权限:
<uses-permission android:name="android.permission.INTERNET"/>
1.3 使用方式
1.3.1 OkHttp基本类的介绍
先来介绍一下使用OkHttp时候,必然会用到的四个类:
- OkHttpClient:OkHttp客户端类,用来构建请求发起类Call,设置超时,设置缓存,设置拦截器等;
- Request:OkHttp请求包装类,用来封装网络请求信息及请求体,比如请求方式(GET、POST、DELETE、PUT等),url,headers,请求参数等,服务器端最终接受并解析的请求消息,就是由该类提供。OkHttpClient获取Call对象时,需要传入Request作为参数;
- Call:发送请求类,该类用来发起OkHttp的网络请求(同步、异步),并将请求结果返回给调用者,该类初始化系统拦截器,并启动拦截器链;
- Response:OkHttp响应包装类,用来封装OkHttp网络请求的结果,包括Http请求响应码,以及服务器端返回的数据(响应体),以及此次请求的Request信息。客户端最终接受并解析的由服务器返回的消息,就是该类提供的。同步请求可以直接获取该类的对象,异步请求通过接口回调获取该类对象。
1.3.2 基本使用步骤如下
- 构建客户端对象OkHttpClient
- 构建请求对象Request
- 生成Call对象
- Call发起请求(同步/异步)
1.4 OkHttp请求
Http请求需要放在开启线程来完成。而同步请求时放在子线程中完成的,所以会阻塞主线程的执行,一般不适用
1.4.1 get的同步请求
1.4.2 get的异步请求
1.4.3 post的同步请求
1.4.4 post的异步请求
MainActivity.java代码:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private TextView tv_show;
private Button get_sync,get_async,post_sync,post_async;
private OkHttpClient okHttpClient = new OkHttpClient();
private String TAG = "zhumeng";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_show = findViewById(R.id.tv_show);
get_sync =findViewById(R.id.get_sync);
get_async =findViewById(R.id.get_async);
post_sync =findViewById(R.id.post_sync);
post_async =findViewById(R.id.post_async);
get_sync.setOnClickListener(this);
get_async.setOnClickListener(this);
post_sync.setOnClickListener(this);
post_async.setOnClickListener(this);
}
public void getSync(View view) {
new Thread() {
@Override
public void run() {
Request request = new Request.Builder()
.url("https://www.httpbin.org/get?a=1&b=2")
.get()
.build();
//准备请求的Call对象
Call call = okHttpClient.newCall(request);
try {
Response response = call.execute();
Log.e(TAG, "getSync: " + response.body().string());
// showResponse("getSync", response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
public void getAsync(View view) {
Request request = new Request.Builder()
.url("https://www.httpbin.org/get?a=1&b=2")
.build();
//准备请求的Call对象
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
Log.e(TAG, "onFailure: " + "与服务器建立连接失败!");
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
if (response.isSuccessful()){
Log.e(TAG, "getAsync: " + response.body().string());
// showResponse("getAsync", response.body().string());
}
}
});
}
public void postSync(View view) {
new Thread(){
@Override
public void run() {
FormBody formBody = new FormBody.Builder()
.add("a","1")
.add("b","2")
.build();
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.e(TAG, "postSync: " + response.body().string());
// showResponse("postSync", response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
public void postAsync(View view) {
FormBody formBody = new FormBody.Builder()
.add("a","1")
.add("b","2")
.build();
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) {
Log.e(TAG, "onFailure: " + "与服务器建立连接失败!");
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
if (response.isSuccessful()){
Log.e(TAG, "postAsync: " + response.body().string());
// showResponse("postAsync", response.body().string());
}
}
});
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.get_sync:
getSync(view);
break;
case R.id.get_async:
getAsync(view);
break;
case R.id.post_sync:
postSync(view);
break;
case R.id.post_async:
postAsync(view);
break;
}
}
// 返回主线程并执行更改UI操作
private void showResponse(String method, String msg) {
runOnUiThread(new Runnable() {
@Override
public void run() {
tv_show.setText(method + "的请求结果为:" + msg);
}
});
}
}
运行结果:
注:将response结果返回到主线程中显示到TextView中时程序奔溃(不知原因)
1.5 OkHttp中post请求的数据格式
协议规定POST提交的数据必须放在请求体中,但协议并没有规定数据必须使用什么编码方式。而常用的数据编码方式有:HTTP content-type | 菜鸟教程 (runoob.com)
常见的请求的数据格式如下:
- Content-Type: application/×-www-form-urlencoded
数据被编码为名称/值对,默认类型; - Content-Type: application/json
提交json数据 - Content-Type: application/octet-stream
提交二进制数据,如果用于文件上传,只能上传一个文件; - Content-Type: multipart/form-data
数据被编码为一条消息,一般用于文件上传;
1.5.1 OkHttp的文件上传
文件的上传这里采用multipart/form-data类型,具体使用如下(在test测试文件夹中,src/test/java/com/example/myokhttp下):
public class FileUploadUnitTest {
@Test
public void fileUpload(){
OkHttpClient okHttpClient = new OkHttpClient();
File file1 = new File("D:\\Android\\Works\\AndroidCommon\\myfileupload\\Files\\1.txt");
File file2 = new File("D:\\Android\\Works\\AndroidCommon\\myfileupload\\Files\\2.txt");
MultipartBody multipartBody = new MultipartBody.Builder()
.addFormDataPart("file1", file1.getName(), RequestBody.create(file1, MediaType.parse("text/plain")))
.addFormDataPart("file2", file2.getName(), RequestBody.create(file2, MediaType.parse("text/plain")))
.addFormDataPart("a", "1")
.build();
Request request = new Request.Builder()
.url("https://www.httpbin.org/post")
.post(multipartBody)
.build();
Call call = okHttpClient.newCall(request);
// call.enqueue(new Callback() {
// @Override
// public void onFailure(@NonNull Call call, @NonNull IOException e) {
// System.out.println("onFailure: " + "服务器建立连接失败");
// }
//
// @Override
// public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
// if (response.isSuccessful()){
// String msg = response.body().string();
// System.out.println("onResponse: " + response.body().string());
// }
// }
// });
try {
Response response = call.execute();
System.out.println(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
}
注:在异步请求中没有输出内容,不知原因
1.5.2 OkHttp的json数据上传
代码如下(这里也是在test测试文件夹中编写代码):
public class FileUploadUnitTest {
@Test
public void jsonTest(){
OkHttpClient okHttpClient = new OkHttpClient();
RequestBody requestBody = RequestBody.create("{\"a\":1,\"b\":2}", 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 response = call.execute();
System.out.println(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
}
1.6 OkHttp的Builder构建者
OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
1.6.1 OkHttp的拦截器
- OkHttpClient okHttpClient = new OkHttpClient.Builder().addInterceptor(new XXX).build();
- OkHttpClient okHttpClient = new OkHttpClient.Builder().addNetworkInterceptor(new XXX).build();
测试代码如下(这里是在test测试文件夹中编写代码):
public class InterceptorUnitTest {
@Test
public void interceptorTest(){
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.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("version: " + chain.request().header("version"));
Response response = chain.proceed(chain.request());
return response;
}
})
.build();
Request request = new Request.Builder()
.url("https://www.httpbin.org/get")
.build();
Call call = okHttpClient.newCall(request);
try {
Response response = call.execute();
System.out.println(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
}
注:这里先输出版本号,即 addInterceptor() 先被执行
1.6.2 OkHttp的缓存与Cookie
OkHttp按照Http协议规则实现了缓存的处理,缓存是比如:当我们发起第一次请求之后,如果后续还需要进行同样的请求,此时如果符合缓存规则,则可以减少与服务器的网络通信,直接从本地文件缓存中读取响应返回给请求者。但是默认情况下,OkHttp的缓存是关闭状态,需要我们开启。
保存在内存的文件中:
// pathname:缓存文件地址
// maxSize:缓存最大容量字节
okHttpclient okHttpclient = new OkHttpclient.Builder( )
.cache(new cache(new File( pathname: "/path/cache"), maxSize: 100))
.build();
Cookie是某些网站为了辨别用户身份,进行会话跟踪(比如确定登陆状态)而储存在用户本地终端上的数据(通常经过加密),由用户客户端计算机暂时或永久保存的信息
保存在本地的测试代码如下(这里是在test测试文件夹中编写代码):
public class CookieUnitTest {
HashMap<String, List<Cookie>> cookies = new HashMap<>();
@Test
public void cookieTest(){
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.cookieJar(new CookieJar() {
@Override
public void saveFromResponse(@NonNull HttpUrl httpUrl, @NonNull List<Cookie> list) {
cookies.put(httpUrl.host(),list);
}
@NonNull
@Override
public List<Cookie> loadForRequest(@NonNull HttpUrl httpUrl) {
List<Cookie> cookie = cookies.get(httpUrl.host());
return cookie==null?new ArrayList<Cookie>():cookie;
}
})
.build();
FormBody formBody = new FormBody.Builder()
.add("username","zhangsan")
.add("password","12345678")
.build();
Request request = new Request.Builder()
.url("https://www.httpbin.org/post")
.post(formBody)
.build();
Call call = okHttpClient.newCall(request);
try {
Response response = call.execute();
System.out.println(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
}
第2章 Retrofit的介绍
2.1 Retrofit的介绍
2.1.1 Retrofit简介
Retrofit地址:square/retrofit (github.com)
Retrofit 是Square公司开发的一款针对Android网络请求的框架,Retrofit2底层基于OkHttp实现的,OkHttp现在已经得到Google官方认可,大量的app都采用OkHttp做网络请求。本文使用Retrofit2进行实例演示。
本质过程:App应用程序通过Retrofit请求网络,实质上是使用Retrofit接口层封装请求参数、Header、Url等信息,之后由okhttp来完成后续的请求工作。在服务端返回数据后,okhttp将原始数据交给Retrofit,Retrofit根据用户需求解析。
2.1.2 Retrofit的优点
-
超级解耦 ,接口定义、接口参数、接口回调不在耦合在一起
-
可以配置不同的httpClient来实现网络请求,如okhttp、httpclient
-
支持同步、异步、Rxjava
-
可以配置不同反序列化工具类来解析不同的数据,如json、xml
-
请求速度快,使用方便灵活简洁
2.2 Retrofit的使用
2.2.1 Retrofit的使用步骤
- 创建工程,引入Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
注:由于Retrofit封装了OkHttp,所以使用到OkHttp时并不需要再引入OkHttp
- 创建业务请求接口HttpRetrofit,具体代码如下
import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.http.Field; import retrofit2.http.FormUrlEncoded; import retrofit2.http.GET; import retrofit2.http.POST; import retrofit2.http.Query; public interface HttpRetrofit { @GET("get") //请求方式,"get"以参数名的形式拼接 Call<ResponseBody> get(@Query("username") String username, @Query("password") String password); @POST("post") //请求方式,"get"以参数名的形式拼接 @FormUrlEncoded //参数的提交格式 Call<ResponseBody> post(@Field("username") String username, @Field("password") String password); }
@GET注解表示GET请求,@Query表示请求参数,将会以key=value(@Query注解参数名称为key,调用传进来的值为value)的方式拼接在url后面.
@POST注解表示POST请求,@FormUrlEncoded将会自动将请求参数的类型设置为application/x-www-form-urlencoded,@FormUrlEncoded注解不能用于Get请求。@Field注解将每一个请求参数都存放至请求体中,还可以添加encoded参数,该参数为boolean型,具体的用法为: @Field(value = “password”, encoded = true) String pwd encoded参数为true的话,key-value-pair将会被编码,即将中文和特殊字符进行编码转换.
- 创建Retrofit对象
Retrofit retrofit = new Retrofit.Builder().baseUrl("https://www.httpbin.org/").build(); HttpRetrofit httpRetrofit = retrofit.create(HttpRetrofit.class);
- 调用请求方法得到Call实例,然后用Call实例完成请求
public void getAsync(View view) { Call<ResponseBody> call = httpRetrofit.get("zhangsan", "123456"); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { if (response.isSuccessful()){ try { Log.e(TAG, "getAsync: " + response.body().string()); } catch (IOException e) { e.printStackTrace(); } } } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { Log.e(TAG, "onFailure: " + "与服务器建立连接失败!"); } }); }
2.3 Retrofit的注解
Retrofit使用大量注解来简化请求,Retrofit将okhttp请求抽象成java接口,使用注解来配置和描述网络请求参数。大概可以分为以下几类,我们先来看看各个注解的含义,再一一去实践解释。
2.3.1 请求方法注解
请求方法注解 | 说明 |
---|---|
@GET | get请求 |
@POST | post请求 |
@PUT | put请求 |
@DELETE | delete请求 |
@PATCH | patch请求,该请求是对put请求的补充,用于更新局部资源 |
@HEAD | head请求 |
@OPTIONS | options请求 |
@HTTP | 通过注解,可以替换以上所有的注解,它拥有三个属性:method、path、hasBody |
2.3.2 请求头注解
请求头注解 | 说明 |
---|---|
@Headers | 用于添加固定请求头,可以同时添加多个,通过该注解的请求头不会相互覆盖,而是共同存在,作用于方法 |
@Header | 作为方法的参数传入,用于添加不固定的header,它会更新已有请求头,作用于方法 |
2.3.3 请求参数注解
请求参数注解 | 说明 |
---|---|
@Body | 多用于Post请求发送非表达数据,根据转换方式将实例对象转化为对应字符串传递参数,比如使用Post发送Json数据,添加GsonConverterFactory则是将body转化为json字符串进行传递 |
@Filed | 多用于Post方式传递参数,需要结合@FromUrlEncoded使用,即以表单的形式传递参数 |
@FiledMap | 多用于Post请求中的表单字段,需要结合@FromUrlEncoded使用 |
@Part | 用于表单字段,Part和PartMap与@multipart注解结合使用,适合文件上传的情况 |
@PartMap | 用于表单字段,默认接受类型是Map<String,RequestBody>,可用于实现多文件上传 |
@Path | 用于Url中的占位符,作用于方法 |
@Query | 用于Get请求中的参数 |
@QueryMap | 与Query类似,用于不确定表单参数 |
@Url | 指定请求路径 |
@GET("get") //请求方式,"get"以参数名的形式拼接
Call<ResponseBody> get(@Query("username") String username, @Query("password") String password, Map<String,String> map);
2.3.4 标记(请求和响应格式)注解
标记类注解 | 说明 |
---|---|
@FromUrlCoded | 表示请求发送编码表单数据,每个键值对需要使用@Filed注解 |
@Multipart | 表示请求发送form_encoded数据(使用于有文件上传的场景),每个键值对需要用@Part来注解键名,随后的对象需要提供值 |
@Streaming | 表示响应用字节流的形式返回,如果没有使用注解,默认会把数据全部载入到内存中,该注解在下载大文件时特别有用 |
2.4 Retrofit中JSON数据的转换
在我们接到服务器的响应后,目前无论是OkHttp还是Retrofit都只能接收到String字符串类型的数据,在实际开发中我们经常需要对字符串进行解析将其转变为一个Java Bean对象。比如服务器响应数据为JSON格式字符串,那么我们可以自己利用GSON库完成反序列化的操作。而Retroft提供了多个转换器使得响应能够完成自动的数据转换。
根据JSON字符串转为JavaBean在线工具:在线JSON字符串转Java实体类(JavaBean、Entity)-BeJSON.com
2.4.1 引入GSON和Retrofit与GSON的解析器
GSON地址:google/gson (github.com)
GSON解析器地址:retrofit/retrofit-converters/gson at master · square/retrofit (github.com)
向build.gradle中添加以下代码:
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.google.code.gson:gson:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
注: Retrofit与GSON的解析器中已经包含有了GSON
2.4.2 转换准备
- 创建JSON对应的JavaBeanJSON对象和data对应的JavaBean对象
- 创建业务请求接口RetrofitService,请求方法的返回值为Call<JavaBeanJSON>
- 创建Retrofit对象(添加JSON的自动解析器)
- 调用请求方法得到Call实例
- 使用Call实例完成请求,得到请求结果Response<JavaBeanJSON> response
- 通过response对象获得请求的字符串stringJSON
2.4.3 JSON数据自动转换为JavaBeanJSON对象
后5步的代码为:
Retrofit retrofit1 = new Retrofit.Builder()
.baseUrl("https://www.httpbin.org/")
.build();
HttpRetrofit httpRetrofit1 = retrofit1.create(HttpRetrofit.class);
Call<JavaBeanJSON> call = httpRetrofit1.get("zhangsan", "123456");
Response<JavaBeanJSON> response = call.execute();
String stringJSON = response.body().string();
JavaBeanJSON javaBeanJSON= new Gson().fromJson(stringJSON, JavaBeanJSON.class);
2.4.4 JSON数据自动转换为JavaBeanJSON对象
后5步的代码为:
Retrofit retrofit2 = new Retrofit.Builder()
.baseUrl("https://www.httpbin.org/")
.addConverterFactory(GsonConverterFactory.create()) //添加自动解析
.build();
HttpRetrofit httpRetrofit2 = retrofit2.create(HttpRetrofit.class);
Call<JavaBeanJSON> call = httpRetrofit1.get("zhangsan", "123456");
Response<JavaBeanJSON> response = call.execute();
JavaBeanJSON javaBeanJSON = response.body();
2.5 Retrofit的适配器
在实际开发中,可能会存在:需要先请求A接口,再请求B接口的情况。比如需要请求获取收藏文章列表,但是需要先登录拿到Cookie才能请求收藏文章列表接口。此时请求就有了先后顺序,为了完成这个功能,我们的实现代码就需要进行嵌套。
Retrofit的接口方法返回类型必须是Call,如果能够将Call改为RxJava中的Observable,对于嵌套的情况,就能得到非常方便优雅的解决。这就是适配器的功能,如果我们想要返回的不是Call,适配器就能够帮助我们转换为其他类型。以RxJava3为例:
RxJava地址:retrofit/retrofit-adapters/rxjava3 at master · square/retrofit (github.com)
RxAndroid地址:Releases · ReactiveX/RxAndroid (github.com)
2.5.1 RxJava的使用步骤
- 引入RxJava
- 修改请求接口HttpRetrofit的方法
- 创建Retrofit对象
- 拿到HttpRetrofit对象
- 执行请求方法
代码如下:
public void rxjavaTest(View view){
HashMap<String, List<Cookie>> cookiesMap = new HashMap<>();
Retrofit retrofit3 = new Retrofit.Builder()
.baseUrl("https://www.wanandroid.com/")
.callFactory(new OkHttpClient.Builder()
.cookieJar(new CookieJar() {
@Override
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
cookiesMap.put(url.host(),cookies);
}
@Override
public List<Cookie> loadForRequest(HttpUrl url) {
List<Cookie> cookie = cookiesMap.get(url.host());
return cookie==null?new ArrayList<Cookie>():cookie;
}
})
.build()) //定制自己的OkHttp
.addConverterFactory(GsonConverterFactory.create()) //添加转换器
.addCallAdapterFactory(RxJava3CallAdapterFactory.create()) //添加适配器
.build();
HttpRetrofit httpRetrofit3 = retrofit3.create(HttpRetrofit.class);
httpRetrofit3.login("zhangsan","123456")
.flatMap(new Function<ResponseBody, Publisher<ResponseBody>>() {
@Override
public Publisher<ResponseBody> apply(ResponseBody responseBody) throws Throwable {
return httpRetrofit3.getArticle(0);
}
}) //第一次请求
.observeOn(Schedulers.io())
.subscribeOn(AndroidSchedulers.mainThread()) //切换主线程,Java中需要使用Schedulers.newThread()
.subscribe(new Consumer<ResponseBody>() {
@Override
public void accept(ResponseBody responseBody) throws Throwable {
Log.e(TAG, responseBody.string());
}
}); //第二次请求
}
2.6 Retrofit的文件上传与下载
实验了两种方式的文件下载,分别为普通的Retrofit和组合RxJava的文件下载
添加网络权限:
<uses-permission android:name="android.permission.INTERNET"/>
引入相关的POI:
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation "com.squareup.retrofit2:adapter-rxjava3:2.9.0"
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
FileService.java代码为:
package com.example.myretrofit;
import io.reactivex.rxjava3.core.Flowable;
import okhttp3.MultipartBody;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
import retrofit2.http.Streaming;
import retrofit2.http.Url;
public interface FileService {
@POST("post")
@Multipart
Call<ResponseBody> upload(@Part MultipartBody.Part file);
@GET
@Streaming //防止文件过大而发生内存溢出
Call<ResponseBody> download(@Url String url);
@GET
@Streaming //防止文件过大而发生内存溢出
Flowable<ResponseBody> downloadRxJava(@Url String url);
}
FileUnitTest.java代码为:
package com.example.myretrofit;
import org.junit.Test;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import io.reactivex.rxjava3.core.Flowable;
import io.reactivex.rxjava3.functions.Consumer;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory;
public class FileUnitTest {
private Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://www.httpbin.org/")
.addCallAdapterFactory(RxJava3CallAdapterFactory.create()) //添加适配器
.build();
private FileService fileService = retrofit.create(FileService.class);
@Test
public void uploadFileTest() throws IOException {
File file1 = new File("D:\\Android\\Works\\AndroidCommon\\myretrofit\\files\\1.txt");
MultipartBody.Part part = MultipartBody.Part
.createFormData("file1", file1.getName(), RequestBody.create(MediaType.parse("text/plain"), file1));
Call<ResponseBody> call = fileService.upload(part);
Response<ResponseBody> response = call.execute();
System.out.println(response.body().string());
}
@Test
public void downloadFileTest() throws IOException {
Call<ResponseBody> call = fileService.download("https://repo1.maven.org/maven2/com/squareup/retrofit2/retrofit/2.9.0/retrofit-2.9.0.jar");
Response<ResponseBody> response = call.execute();
InputStream inputStream = response.body().byteStream();
FileOutputStream fileOutputStream = new FileOutputStream("D:\\Android\\Works\\AndroidCommon\\myretrofit\\libs\\retrofit-2.9.0.jar");
int len;
byte[] buffer = new byte[1024];
while ((len = inputStream.read(buffer)) != -1){
fileOutputStream.write(buffer, 0, len);
}
inputStream.close();
fileOutputStream.close();
}
@Test
public void downloadRxJavaFileTest() throws InterruptedException {
Flowable<ResponseBody> flowable = fileService.downloadRxJava("https://repo1.maven.org/maven2/com/squareup/retrofit2/retrofit/2.9.0/retrofit-2.9.0.jar");
flowable.subscribe(new Consumer<ResponseBody>() {
@Override
public void accept(ResponseBody responseBody) throws Throwable {
InputStream inputStream = responseBody.byteStream();
FileOutputStream fileOutputStream = new FileOutputStream("D:\\Android\\Works\\AndroidCommon\\myretrofit\\libs\\retrofit-2.9.0(1).jar");
int len;
byte[] buffer = new byte[1024];
while ((len = inputStream.read(buffer)) != -1){
fileOutputStream.write(buffer, 0, len);
}
System.out.println("下载成功");
inputStream.close();
fileOutputStream.close();
}
});
Thread.sleep(10000); //等待下载
}
}
注意包名及项目结构: