Android中OkHttp和Retrofit的使用

第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 基本使用步骤如下

  1. 构建客户端对象OkHttpClient
  2. 构建请求对象Request
  3. 生成Call对象
  4. 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的使用步骤

  1. 创建工程,引入Retrofit
        implementation 'com.squareup.retrofit2:retrofit:2.9.0'

    注:由于Retrofit封装了OkHttp,所以使用到OkHttp时并不需要再引入OkHttp

  2. 创建业务请求接口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将会被编码,即将中文和特殊字符进行编码转换.

  3. 创建Retrofit对象
            Retrofit retrofit = new Retrofit.Builder().baseUrl("https://www.httpbin.org/").build();
            HttpRetrofit httpRetrofit = retrofit.create(HttpRetrofit.class);
  4. 调用请求方法得到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 转换准备

  1. 创建JSON对应的JavaBeanJSON对象和data对应的JavaBean对象
  2. 创建业务请求接口RetrofitService,请求方法的返回值为Call<JavaBeanJSON>
  3. 创建Retrofit对象(添加JSON的自动解析器)
  4. 调用请求方法得到Call实例
  5. 使用Call实例完成请求,得到请求结果Response<JavaBeanJSON> response
  6. 通过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的使用步骤

  1. 引入RxJava
  2. 修改请求接口HttpRetrofit的方法
  3. 创建Retrofit对象
  4. 拿到HttpRetrofit对象
  5. 执行请求方法

代码如下:

    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); //等待下载
    }
}

注意包名及项目结构:

猜你喜欢

转载自blog.csdn.net/weixin_57542177/article/details/124077974