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. 参考文献

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

相关推荐
drebander5 分钟前
使用 Java Stream 优雅实现List 转化为Map<key,Map<key,value>>
java·python·list
乌啼霜满天2499 分钟前
Spring 与 Spring MVC 与 Spring Boot三者之间的区别与联系
java·spring boot·spring·mvc
tangliang_cn14 分钟前
java入门 自定义springboot starter
java·开发语言·spring boot
程序猿阿伟15 分钟前
《智能指针频繁创建销毁:程序性能的“隐形杀手”》
java·开发语言·前端
找藉口是失败者的习惯21 分钟前
从传统到未来:Android XML布局 与 Jetpack Compose的全面对比
android·xml
Grey_fantasy24 分钟前
高级编程之结构化代码
java·spring boot·spring cloud
弗锐土豆31 分钟前
工业生产安全-安全帽第二篇-用java语言看看opencv实现的目标检测使用过程
java·opencv·安全·检测·面部
Elaine20239132 分钟前
零碎04 MybatisPlus自定义模版生成代码
java·spring·mybatis
小小大侠客1 小时前
IText创建加盖公章的pdf文件并生成压缩文件
java·pdf·itext
一二小选手1 小时前
【MyBatis】全局配置文件—mybatis.xml 创建xml模板
xml·java·mybatis