• Android网络通讯之Retrofit


    Retrofit本身并不支持网络通讯。

    Retrofit封装了OkHttp,让网络请求更加简单。

    Retrofit通过接口+注解的方式,封装了重复的代码。

    封装了Gson,可以直接将返回的json字符串转换成Java对象

    结合rxjava,实现响应式编程

    使用Retrofit进行网络通讯就好像使用SpringCloud中的Openfeign

    官网地址:https://square.github.io/retrofit/

    使用步骤

    准备

    1、在build.gradle中引入依赖implementation 'com.squareup.retrofit2:retrofit:2.9.0'

    2、在AndroidManifest.xml中添加网络请求权限

    一、简单使用

    使用 https://www.httpbin.org/ 作为我们的测试接口地址

    1、创建接口类

    @POST 声明为post请求,实际请求地址会拼接成 https://www.httpbin.org/post, 通过@FormUrlEncoded和@Field声明表单请求参数;@Body声明Body参数。

    @GET 声音为get请求,实际请求地址会拼接成 https://www.httpbin.org/get, 通过@Query声明请求参数。

    import okhttp3.RequestBody;
    import okhttp3.ResponseBody;
    import retrofit2.Call;
    import retrofit2.http.Body;
    import retrofit2.http.Field;
    import retrofit2.http.FormUrlEncoded;
    import retrofit2.http.GET;
    import retrofit2.http.POST;
    import retrofit2.http.Query;
    
    public interface HttpbinService {
    
        @POST("post")
        @FormUrlEncoded
        Call<ResponseBody> post(@Field("username") String username, @Field("password")  String pwd);
    
    
        @GET("get")
        Call<ResponseBody> get(@Query("username") String username,@Query("password")  String pwd);
    
        @POST("post")
        Call<ResponseBody> postBody(@Body RequestBody requestBody);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    2、在Android程序中使用
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import androidx.appcompat.app.AppCompatActivity;
    import java.io.IOException;
    import okhttp3.ResponseBody;
    import retrofit2.Call;
    import retrofit2.Callback;
    import retrofit2.Retrofit;
    
    public class MainActivity extends AppCompatActivity {
    
        private Retrofit retrofit;
        private HttpbinService httpbinService;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            retrofit = new Retrofit.Builder().baseUrl("https://www.httpbin.org/").build();
            httpbinService = retrofit.create(HttpbinService.class);
        }
    
    
        /**
         * 异步Get请求
         */
        public void getAsync(View view) {
            Call<ResponseBody> resp = httpbinService.get("test", "123123");
            resp.enqueue(new Callback<ResponseBody>() {
                @Override
                public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) {
                    try {
                        Log.d("test", "getAsync: " + response.body().string());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
    
                @Override
                public void onFailure(Call<ResponseBody> call, Throwable t) {
                    Log.d("test", "getAsync error: " + t.getMessage());
                }
            });
        }
    
        /**
         * 异步Post请求
         */
        public void postAsync(View view) {
    
            Call<ResponseBody> resp = httpbinService.post("test", "123123");
            resp.enqueue(new Callback<ResponseBody>() {
                @Override
                public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) {
                    try {
                        Log.d("test", "postAsync: " + response.body().string());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
    
                @Override
                public void onFailure(Call<ResponseBody> call, Throwable t) {
                    Log.d("test", "postAsync error: " + t.getMessage());
                }
            });
    
        }
    
    }
    
    • 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
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    二、配合Gson 以及 Rxjava,返回java对象和响应式编程

    在build.gradle中引入依赖

    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
    implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'
    implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
    
    • 1
    • 2
    • 3

    本例中使用 https://www.wanandroid.com/ 测试接口调用

    1、添加测试接口类
    import io.reactivex.rxjava3.core.Flowable;
    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.Path;
    
    public interface WanAndroidTestService {
    
        /**正常返回 Java对象 */
        @POST("/user/login")
        @FormUrlEncoded
        Call<LoginUser> login(@Field("username") String username, @Field("password") String password);
    
        /**通过RxJava响应式返回 Java对象 */
        @POST("/user/login")
        @FormUrlEncoded
        Flowable<LoginUser> login2(@Field("username") String username, @Field("password") String password);
    
        /**通过RxJava响应式返回 ResponseBody */
        @GET("lg/collect/list/{pageNum}/json")
        Flowable<ResponseBody> getArticle(@Path("pageNum") int pageNum);
    }
    
    • 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
    2、测试使用,返回Java对象

    添加转换器 addConverterFactory(GsonConverterFactory.create())

    public class WanAndroidUnitTest {
    
        Retrofit retrofit = new Retrofit.Builder().baseUrl("https://www.wanandroid.com/")
                .addConverterFactory(GsonConverterFactory.create())//添加转换器
                .build();
        WanAndroidTestService wanAndroidService = retrofit.create(WanAndroidTestService.class);
    
        @Test
        public void login() throws IOException {
            Response<LoginUser> response = wanAndroidService.login("xxxxx", "xxxxx").execute();
            LoginUser loginUser = response.body();
            System.out.println("loginUser = " + loginUser);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    3、配合RxJava使用,并使用cookie

    添加Cookie支持
    添加Gson转换器addConverterFactory(GsonConverterFactory.create())
    添加RxJava适配器addCallAdapterFactory(RxJava3CallAdapterFactory.create())

    本示例代码使用www.wanandroid.com的开放接口,可以自行去注册一个账号用于测试

    import io.reactivex.rxjava3.functions.Consumer;
    import io.reactivex.rxjava3.functions.Function;
    import io.reactivex.rxjava3.schedulers.Schedulers;
    import okhttp3.Cookie;
    import okhttp3.CookieJar;
    import okhttp3.HttpUrl;
    import okhttp3.OkHttpClient;
    import okhttp3.ResponseBody;
    import retrofit2.Retrofit;
    import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory;
    import retrofit2.converter.gson.GsonConverterFactory;
    
    public class RetrofitCookieUnitTest {
    
        Map<String, List<Cookie>> cookies = new HashMap<>();
        Retrofit retrofit = new Retrofit.Builder().baseUrl("https://www.wanandroid.com/")
                // 添加Cookie支持
                .callFactory(new OkHttpClient.Builder().cookieJar(new CookieJar() {
                    @Override
                    public void saveFromResponse(HttpUrl url, List<Cookie> list) {
                        cookies.put(url.host(), list);
                    }
    
                    @Override
                    public List<Cookie> loadForRequest(HttpUrl url) {
                        List<Cookie> list = cookies.get(url.host());
                        return list == null ? new ArrayList<>() : list;
                    }
                }).build())
                //添加Gson转换器
                .addConverterFactory(GsonConverterFactory.create())
                //添加RxJava适配器
                .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
                .build();
        WanAndroidTestService androidTestService = retrofit.create(WanAndroidTestService.class);
    
    
        @Test
        public void rxTest() throws IOException {
            androidTestService.login2("xxxxx", "xxxxx")
                    // 登录成功后,执行下一步操作,查询收藏文章列表
                    .flatMap(new Function<LoginUser, Publisher<ResponseBody>>() {
                        @Override
                        public Publisher<ResponseBody> apply(LoginUser loginUser) throws Throwable {
                            return androidTestService.getArticle(0);
                        }
                    })
                    .observeOn(Schedulers.io())
                    // 如果是Android环境需要切换到Android的主线程
                    // .subscribeOn(AndroidSchedulers.mainThread())
                    // 这里是Junit,切换到新的线程
                    .subscribeOn(Schedulers.newThread())
                    // 订阅返回结果,并打印输出
                    .subscribe(new Consumer<ResponseBody>() {
                        @Override
                        public void accept(ResponseBody responseBody) throws Throwable {
                            System.out.println(responseBody.string());
                        }
                    });
    
            while (true) {
            }
    
        }
    }
    
    • 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
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    4、文件上传、下载

    文件上传

    使用注解@Multipart@Part实现文件上传

    1)创建请求接口

    import okhttp3.MultipartBody;
    import okhttp3.ResponseBody;
    import retrofit2.Call;
    import retrofit2.http.Multipart;
    import retrofit2.http.POST;
    import retrofit2.http.Part;
    
    public interface UploadFileService {
        @POST("post")
        @Multipart
        Call<ResponseBody> upload(@Part MultipartBody.Part part);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2)编写测试代码

    public class UploadFileTest {
        Retrofit retrofit = new Retrofit.Builder().baseUrl("https://www.httpbin.org/").build();
        UploadFileService uploadFileService = retrofit.create(UploadFileService.class);
    
        @Test
        public void upload() throws IOException {
            File file1 = new File("d:/temp/1.txt");
            MultipartBody.Part part = MultipartBody.Part.createFormData("file1", file1.getName(),
                    RequestBody.create(MediaType.parse("text/plain"), file1));
            Call<ResponseBody> resp = uploadFileService.upload(part);
            System.out.println("resp = " + resp.execute().body().string());
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    文件下载
    • Call 常规下载
    • Flowable 通过RxJava方式下载,需要添加RxJava适配器

    如果下载的文件很大,需要增加注解 @Streaming避免出现内存溢出

    1)创建请求接口

    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 UploadFileService {
        @POST("post")
        @Multipart
        Call upload(@Part MultipartBody.Part part);
    
        @GET
        Call download(@Url String url);
    
        @Streaming
        @GET
        Flowable downloadRxJava(@Url String url);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    2)创建测试用例

    import org.junit.Test;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import io.reactivex.rxjava3.functions.Consumer;
    import io.reactivex.rxjava3.functions.Function;
    import okhttp3.ResponseBody;
    import retrofit2.Response;
    import retrofit2.Retrofit;
    import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory;
    
    public class DownloadFileTest {
        Retrofit retrofit = new Retrofit.Builder().baseUrl("https://www.httpbin.org/")
                //添加RxJava适配器
                .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
                .build();
        UploadFileService uploadFileService = retrofit.create(UploadFileService.class);
    
        @Test
        public void download() throws IOException {
            Response<ResponseBody> response = uploadFileService.download("https://www.dasouji.com/wp-content/uploads/2020/12/@ukwanghyun.jpg")
                    .execute();
            if (!response.isSuccessful()) {
                System.out.println("下载失败," + response.code());
                return;
            }
            InputStream inputStream = response.body().byteStream();
    
            FileOutputStream fos = new FileOutputStream("d:/temp/3.jpg");
            int len;
            byte[] buffer = new byte[4096];
            while ((len = inputStream.read(buffer)) != -1) {
                fos.write(buffer, 0, len);
            }
            fos.close();
            inputStream.close();
            System.out.println("下载完成," + response.code());
        }
    
        @Test
        public void downloadRxJava() throws IOException {
            uploadFileService.downloadRxJava("https://www.dasouji.com/wp-content/uploads/2020/12/@ukwanghyun.jpg")
                    // 将结果映射成File对象
                    .map(new Function<ResponseBody, File>() {
                        @Override
                        public File apply(ResponseBody responseBody) throws Throwable {
                            InputStream inputStream = responseBody.byteStream();
                            File file = new File("d:/temp/4.jpg");
                            FileOutputStream fos = new FileOutputStream(file);
                            int len;
                            byte[] buffer = new byte[4096];
                            while ((len = inputStream.read(buffer)) != -1) {
                                fos.write(buffer, 0, len);
                            }
                            fos.close();
                            inputStream.close();
                            System.out.println("下载完成!");
                            return file;
                        }
                    })
                    // 获取下载结果
                    .subscribe(new Consumer<File>() {
                        @Override
                        public void accept(File file) throws Throwable {
                            System.out.println("获取下载文件!" + file.getName());
                        }
                    });
            while (true) {
            }
        }
    }
    
    • 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
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    Retrofit的注解

    • 方法注解:@GET,@POST,@PUT,@DELETE,@PATH,@HEAD,@OPTIONS,@HTTP
    • 标记注解:@FormUrlEncoded,@Multipart,@Streaming
    • 参数注解:@Query,@Field,@Body,@FieldMap,@QueryMap,@Part,@PartMap
    • 其他注解:@Path,@Header,@Headers,@Url
  • 相关阅读:
    如何将超大文件压缩到最小
    多元函数泰勒公式(含黑塞矩阵)
    【JS】你不知道的 console 命令
    【LeetCode】1161.最大层内元素和
    leetcode:152. 乘积最大子数组
    【Vue3+Tres 三维开发】02-Debug
    postgresql源码学习(38)—— 备份还原② - do_pg_start_backup函数
    Mybatis入门,day2,动态SQL
    进程的概念,组成和特征(PCB)
    Java面向对象8——接口(内含IDEA中有关创建接口的创建说明)
  • 原文地址:https://blog.csdn.net/wlddhj/article/details/127775702