Retrofit系列(一)——Retrofit的基础使用

1. Retrofit概述

Retrofit是一个RESTful的HTTP网络请求框架的封装,是在OkHttp基础上进一步开发出来的。

网络请求的本质工作是OKHttp实现的,Retrofit仅负责网络请求接口的封装

OKHttp主要是底层通信的实现,而Retrofit则是对上层接口的封装。

Retrofit本质过程

应用程序通过Retrofit请求网络,实质上使用Retrofit接口层封装请求参数、Header以及Url等信息,之后由okhttp来完成后续的请求工作。在服务端返回数据后,okhttp将数据交给Retrofit,Retrofit根据用户需求解析。

Retrofit优化了网络使用过程:

2. Retrofit设计思想及其优点

优点

  1. 解耦,接口定义、接口回调、接口参数不耦合在一起。
  2. 可以配置不同的httpClient来实现网络请求,如okhttp、httpclient。
  3. 支持同步、异步、RxJava。
  4. 可以配置不同反序列化工具类来解析不同的数据,如json、xml。
  5. 请求速度快、使用方便灵活简洁。

Retrofit的基本设计思想:

1.同一款应用程序中所发起的网络请求绝大多数指向的是同一个服务器域名。

首先我们配置好一个根路径,然后在指定服务器接口地址时只需要使用相对路径即可,这样就不用每次都指定完整的URL地址。

2.服务器提供的接口通常是可以根据功能来归类的。

Retrofit允许我们对接口进行归类,将功能同属一类的服务器接口定义到同一个接口文件中。我们完全不需要关心网络通信的细节,只需在接口文件中声明一系列方法和返回值,然后通过注解的方式指定该方法对应哪个服务器接口,以及需要提供哪些参数。

3.开发者习惯于,调用一个接口,获取它的返回值。

当我们在程序中调用该方法时,Retrofit会自动向对应的服务器接口发出请求,并将响应的数据解析成返回值声明的类型。

3. Retrofit的基本使用

添加依赖:

implementation 'com.squareup.retrofit2:retrofit:2.6.1'

implementation 'com.squareup.retrofit2:converter-gson:2.1.0'

创建接受服务器返回数据的类

JSON 复制代码
{
    "code": "000000",
    "description": "",
    "responseBody": {
        "user_id": "121293",
        "user_name": "Liang",
        "user_age": "20"
    }
}

根据JSON数据接口新建一个一个User类,数据回调类:

java 复制代码
public class User{
    private String code;//状态码
    private String description;//状态信息
    public R responseBody;//实体数据
    public static class R{
        private String user_id;
        private String user_name;
        private String user_age;

        public String getUser_id() {
            return user_id;
        }

        public void setUser_id(String user_id) {
            this.user_id = user_id;
        }

        public String getUser_name() {
            return user_name;
        }

        public void setUser_name(String user_name) {
            this.user_name = user_name;
        }

        public String getUser_age() {
            return user_age;
        }

        public void setUser_age(String user_age) {
            this.user_age = user_age;
        }
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

创建网络接口类

根据接口的功能进行归类,创建不同种类的接口文件,并在其中定义对应具体服务器接口方法。新建接口,通常Retrofit的接口文件建议以具体的功能种类名开头,并以Service结尾

OkHttp使用构建者模式创建Request,Retrofit中使用了注解。

Retrofit将Okhttp请求抽象成Java接口,采用注解描述和配置网络请求参数,封装Url地址和网络请求数据,用动态代理将该接口的注解翻译成一个Http请求,最后执行Http请求。

getData()方法返回值必须声明成Retrofit中内置的Call类型,并通过泛型来指定服务器响应的数据应该为什么对象。

如果想直接获取ResponseBody中的内容,可以定义网络请求返回值为Call<ResponseBody>,ResponseBody是请求网络后返回的原始数据。

java 复制代码
public interface AppService {
    @FormUrlEncoded
    @POST("app/test/user/login")
    Call<ResponseBody> getPostData(@Field("user") String nameStr, @Field("pwd") String pwdStr);
    
    @GET("app/test/user/get_user")//Retrofit发出一条Get请求,此处传入的为相对路径
    Call<User> getData();
    
    @GET("user")//我们需要传入的id=10006,name="Liang",那么拼接参数后就是完整的请求地址:https://api.github.com/user?id=10006&name=Liang。
    Call<ResponseBody> getData2(@Query("id") long idLon, @Query("name") String nameStr);
    
    @GET("user")
    Call<ResponseBody> getData3(@QueryMap Map<String, Object> map);
}
java 复制代码
Map<String, Object> map = new HashMap<>();
map.put("id", 10006);
map.put("name", "Liang");
Call<ResponseBody> call = retrofit.create(Api.class).getData3(map);

登录按钮,传入用户名和密码,POST请求

java 复制代码
button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        String user1 = user.getText().toString();
        String pwd1 = pwd.getText().toString();
        if(user.getText().length() != 11 || pwd1.equals("")){
            //Toast.makeText(LoginActivity.this,"用户名或密码有误,请重新输入",Toast.LENGTH_SHORT).show();
            AlertDialog.Builder dialog = new AlertDialog.Builder(LoginActivity.this);
            dialog.setTitle("登录失败");
            dialog.setMessage("用户名或密码错误,请重新输入。");
            dialog.setCancelable(false);
            dialog.setPositiveButton("确定",new DialogInterface.OnClickListener(){
                @Override
                public void onClick(DialogInterface dialog, int which) {
                }
            });
            dialog.show();
        }else{
            HttpUtil.postRequest(user1,pwd1);
            Intent intent = new Intent(LoginActivity.this,UserActivity.class);
            startActivity(intent);
        }
    }

Get请求将接口信息展现在页面上

less 复制代码
HttpUtil.getRequest(new HttpUtil.UserCallBack() {
    @Override
    public void callBack(User user) {
        int i = 1;
        user_id.setText(String.format("%d", i));
        Log.e("TAG", String.format("-----%s", Thread.currentThread().getName()));
        code.setText(user.getCode());
        description.setText(user.getDescription());
        user_id.setText(user.responseBody.getUser_id());
        user_name.setText(user.responseBody.getUser_name());
        user_age.setText(user.responseBody.getUser_age());
    }
});

Retrofit使用构建者模式创建实例,首先使用Retrofit.Builder来构建一个Retrofit对象,baseUrl()addConverterFactory()两个方法必须调用。

Retrofit序列化数据仅需添加addConverterFactory()即可。

有了Retrofit对象后,调用create()方法(Retrofit核心),并传入具体的Service接口所对应的Class类型,创建一个该接口的动态代理对象appService(返回一个对象)。有了动态代理对象后我们就可以随意调用接口中定义的所有方法。(动态代理可以参考这篇文章------代理模式详解)

appService.getData()返回一个Call<User>对象,再调用enqueue(),Retrofit就会根据注解中配置的服务器接口地址去进行网络请求了,服务器响应的数据会回调到enqueue()方法中传入的CallBack实现里面。

当发起请求的时候,Retrofit会自动在内部开启子线程,当数据回调到CallBack中后,Retrofit又会切换到主线程

java 复制代码
public class HttpUtil {

    private static String userLocal;
    
    public static void postRequest(String user, String pwd){
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://api.tb.org/")//指定根路径,必须以"/"结尾
                .addConverterFactory(GsonConverterFactory.create())//指定解析数据时使用的转换库
                .build();
        AppService service = retrofit.create(AppService.class);
        service.getPostData(user,pwd). enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
               // Toast.makeText(UserActivity.class, "post回调成功:异步执行", Toast.LENGTH_SHORT).show();
                Log.d("HttpUtil","成功");
            }
            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t){
                t.printStackTrace();
            }
        });
    }

    public static void getRequest(UserCallBack userCallBack){
        Retrofit retrofit = new Retrofit.Builder().baseUrl("http://api.tb.org/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        AppService appService = retrofit.create(AppService.class);
        appService.getData().enqueue(new Callback<User>() {//appService.getData()返回一个Call<User>对象,再调用enqueue(),Retrofit就会根据注解中配置的服务器接口地址去进行网络请求了
            @Override
            public void onResponse(@NonNull Call<User> call, @NonNull Response<User> response) {
                userCallBack.callBack(response.body());//Retrofit解析后的对象
            }
            @Override
            public void onFailure(@NonNull Call<User> call, @NonNull Throwable t){
                t.printStackTrace();
            }
        });
    }

    interface UserCallBack{
        void callBack(User user);
    }
}

获取Serivce的动态代理太麻烦,将这部分功能封装起来。

java 复制代码
public class ServiceUtil {
    private static Retrofit retrofit = null;
    private static final String url = "https://dev.usemock.com/";
    public static <T> T getService(Class<T> serviceClass){
        if(retrofit == null){
            retrofit = new Retrofit.Builder().baseUrl(url)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }
        return retrofit.create(serviceClass);
    }
}

4. 参考文献

郭霖《第一行代码 第三版》

相关推荐
七星静香19 分钟前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
Jacob程序员20 分钟前
java导出word文件(手绘)
java·开发语言·word
ZHOUPUYU20 分钟前
IntelliJ IDEA超详细下载安装教程(附安装包)
java·ide·intellij-idea
stewie623 分钟前
在IDEA中使用Git
java·git
problc32 分钟前
Flutter中文字体设置指南:打造个性化的应用体验
android·javascript·flutter
Elaine20239139 分钟前
06 网络编程基础
java·网络
G丶AEOM40 分钟前
分布式——BASE理论
java·分布式·八股
落落鱼201341 分钟前
tp接口 入口文件 500 错误原因
java·开发语言
想要打 Acm 的小周同学呀42 分钟前
LRU缓存算法
java·算法·缓存
镰刀出海1 小时前
Recyclerview缓存原理
java·开发语言·缓存·recyclerview·android面试