初识Retrofit2

2019-11-26  本文已影响0人  做梦枯岛醒

文章首发自本人语雀,转载请注明出处(本文url)

Type-safe HTTP client for Android and Java by Square, Inc.

上面一句话引用自Retrofit的广告语,意思是Retrofit是一个Java和Android平台上的类型安全的Http客户端,是由Square公司开发的。

github地址:https://github.com/square/retrofit

官方文档:https://square.github.io/retrofit/

首先确定一个问题,Okhttp也是Square开发的网络请求框架,那么Retrofit跟他的关系是什么呢?可以看一张图。

image.png

上面一张图已经给出了答案,实际上Retrofit底层还是Okhttp,数据是经过Retrofit层到达Okhttp再与后端进行交互的,那么Retrofit又是什么角色存在的呢?

初识Retrofit

官方文档上给了几个这样的代码段

public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();

GitHubService service = retrofit.create(GitHubService.class);
Call<List<Repo>> repos = service.listRepos("octocat");

第一段是定义了一个接口,其中使用@GET注解标识了这是一个GET请求,后面是一个请求的路径,其中{user}部分内容可以被@Path("user") String user 参数所替换,最后返回的是一个携带数据的Call,这也是Okhttp里的Call,用过Okhttp的同学想必都知道。

第二段就是Retrofit的初始化,简单的初始化是一个构造者模式,传入一个连接即可。使用create方法可以创建一个请求,最后调用listRepos方法传入参数就可以实现请求。

第三段就是调用的部分

 githubService.listRepos("surine").enqueue(new Callback<List<String>>() {
                    @Override
                    public void onResponse(Call<List<String>> call, Response<List<String>> response) {

                    }

                    @Override
                    public void onFailure(Call<List<String>> call, Throwable t) {

                    }
                });

假如说我这样写,你可能就明白了,这就是Okhttp的那一套。

看了上面一段例子,你会发现使用Retrofit的话,我们可以通过接口来管理API,而不必写一堆全局变量改起来还很麻烦,如果后端使用Restful的话,客户端也可以使用Retrofit来维护一套Restful风格的API。

RestfulAPI

[POST]     http://test.com/users   // 新增
[GET]      http://test.com/users/xiaoming // 查询
[PATCH]    http://test.com/users/xiaoming // 更新
[PUT]      http://test.com/users/xiaoming // 覆盖,全部更新
[DELETE]   http://test.com/users/xiaoming // 删除

Restful致力于解决API多的问题,使用传统的API实现上述几个动作需要5个API,但是Restful只需要维护一个,他的操作是通过Http的协议来规范的。

这里的一个是指http://test.com/users通常一个Url包含这几部分。

https://www.baidu.com/s?wd=Android%20Slide&rsv_spt=1

协议https + 域名/www.baidu.com + 路径 /s  + 参数

而在Retrofit里这几部分的配置也是非常简单。

域名:

.baseUrl("https://api.github.com/")配置协议和域名

路径:

@GET("users/{user}/repos")配置了协议动作和路径,当然也可以如下这样写。@PATCH("users/{user}/repos")这样的话根据Restful实现的就是更新功能了。(这里就是举一个小栗子)

参数:

参数实现有几种形式,比如在上面的代码段中看到了

   @GET("users/{user}/repos")
    Call<List<String>> listRepos(@Path("user") String user);

是使用Path注解来填充参数的,这里是替换路径中的{user},用法比较特殊

真正的参数可以这样来配。

  @GET("users/{user}/repos")
  Call<List<String>> listRepos(@Path("user") String user, @Query("page")int page);

上述代码实现的最终访问就是:https://api.github.com/users/xxxx/repos?page = xx

当我们参数很多的情况,写一个参数很多的方法不太合理,所以可以考虑使用键值对来实现。

@GET("users/{user}/repos")
    Call<List<String>> listRepos(@Path("user") String user, @QueryMap Map<String, String> options);

通常这是一个比较不错的选择。

此外,当我们使用POST方式请求的时候,可以这样写。

 @POST("/repos")
 Call<List<String>> add(@Body Repo repo);

使用Body注解直接上传一个对象,添加Json转换时,对应数据将会被转成Json发送

当然还有很多种注解形式,在后面补充。

其他注解

 @FormUrlEncoded
    @HTTP(method = "POST")
    Call<String> get(@Url String url, @Field("page")int page);

上述代码中出现了几个新的注解。

@FormUrlEncoded :只能用于POST请求,会将请求参数调整为application/x-www-form-urlencoded格式。

@HTTP:可扩展常见的GET,POST,DELETE等方法,包含几个参数,method(方法名),path(路径),hasBody(是否有请求体)

@Url:可以直接填入请求地址,这样在方法注解中可以不填,使用这种方式的优先级比配置baseUrl大,传入此参数后可以直接使用这个url进行请求。

@Field :请求参数,与Query类似,他也有对应的FieldMap,只不过Field用于POST,Query用于GET。

 @GET("user")
    Call<String> getUserName(@Header("Authorization") String authorization);

   @GET("user")
    @Headers("Cache-Control: max-age=640000")
    Call<String> getUserName();

@Header:header可以用来加请求头。

@Headers:功能与Header类似

此外还有一些文件上传下载,流之类的标记,在这里就不详细提了。

实例

image.png

上面是一个金山词霸的翻译接口,使用Retrofit来实现一下。

这里我会从最初的封装开始。下面的代码有的是我项目中的一些工具类,相关内容我都会注释的,具体的实现可以替换。

Retrofit封装
public class Retrofits {
    //singleton 单例模式,你可以使用自己写的
    public static AbctractSingleTon<Retrofits> abt = new AbctractSingleTon<Retrofits>() {
        @Override
        protected Retrofits newObj(Bundle bundle) {
            return new Retrofits();
        }
    };

    //retrofit对象
    private static Retrofit retrofit;


    //初始化的时候就进行构造,分别是配置baseUrl,配置Okhttp客户端(稍后看OkNet类)
    //配置Json转换器,这边用的是Gson
    private Retrofits() {
        retrofit = new Retrofit.Builder()
                .baseUrl("http://fy.iciba.com")
                .client(OkNet.abt.getInstance().getClient())
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }

    //返回retrofit的方法
    public static Retrofit get() {
        return retrofit;
    }

    //创建api服务用的
    public <T> T create(Class<T> service) {
        return retrofit.create(service);
    }
}

在上面的类中用到了Okhttp的client,是从OkNet中拿来了。

public class OkNet {
    
    //单例模式
    public static AbctractSingleTon<OkNet> abt = new AbctractSingleTon<OkNet>() {
        @Override
        protected OkNet newObj(Bundle bundle) {
            return new OkNet();
        }
    };
    
    //okhttp客户端
    private static OkHttpClient mOkHttpClient;
    //cookie管理
    private static ConcurrentHashMap<String, List<Cookie>> cookieStore = new ConcurrentHashMap<>();

    //打印请求的信息,这边用到的是官方的日志拦截器,原理就是用的Okhttp的请求拦截
    HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
        @Override
        public void log(String message) {
            //print retrofit log
            Logs.d("TustBox = " + message);
        }
    });


    private OkNet() {
        //配置要打印哪内容,BODY意思是全部打印
        loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        //okhttp客户端配置日志拦截,cookie管理,超时等。
        mOkHttpClient = new OkHttpClient.Builder()
                .connectTimeout(5, TimeUnit.SECONDS)
                .addInterceptor(loggingInterceptor)
                .cookieJar(new CookieJar() {
                    @Override
                    public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
                        cookieStore.put(url.host(), cookies);
                    }

                    @Override
                    public List<Cookie> loadForRequest(HttpUrl url) {
                        List<Cookie> cookies = cookieStore.get(url.host());
                        return cookies != null ? cookies : new ArrayList<Cookie>();
                    }
                })
                .build();
    }

    //返回okhttp客户端
    public OkHttpClient getClient() {
        return mOkHttpClient;
    }

}

下面是一个参数类的封装,可以直接创建参数Map

public class Param {
    private final ArrayMap<String, Object> map;

    /**
     * init a param manager
     * */
    public static Param ins(){
        return new Param();
    }

    private Param(){
        map = new ArrayMap<>();
    }

    /**
     * put a param
     * */
    public Param put(String key,Object value){
        if (value != null) {
            map.put(key, value);
        }
        return this;
    }

    public Map<String, Object> param(){
        return map;
    }
}

下面是一个Loader类,是给最顶层提供封装服务的。

public class Loader extends BaseLoader {

    //单例模式
    public static AbctractSingleTon<Loader> abt = new AbctractSingleTon<Loader>() {
        @Override
        protected Loader newObj(Bundle bundle) {
            return new Loader();
        }
    };

    //获取服务,这里通过Param传入参数,其中wd是要翻译的内容
    public Call<Word> getTrans(String wd){
        return getService(TestService.class).getTrans(Param.ins()
                .put("a","fy")
                .put("f","auto")
                .put("t","auto")
                .put("w",wd)
                .param()
        );
    }

    //接口
    interface TestService {
        @GET("/ajax.php")
        Call<Word> getTrans(@QueryMap Map<String,Object> ip);

    }

}

最后就是使用了。

  Loader.abt.getInstance().getTrans("你好").enqueue(new Callback<Word>() {
                    @Override
                    public void onResponse(Call<Word> call, Response<Word> response) {
                        Toasts.shortShow(response.toString());
                    }

                    @Override
                    public void onFailure(Call<Word> call, Throwable t) {

                    }
                });

其中前面是调用,后面是熟悉的Okhttp的回调。而运行结果我们可以在控制台看到。这也是我们刚才设置日志所产生的效果。

image.png

由于Gson的存在,我们可以直接从请求结果中取对象。

Logs.d(response.body().toString());

总结

上面的内容将了Retrofit的几种注解,然后给了一个小的Demo来讲Retrofit的使用,但是实际开发中大部分项目都会选择和Rxjava来结合开发,后续的文章我会写两者结合的使用。

参考链接:

https://www.loongwind.com/archives/242.html

https://juejin.im/entry/57a97dc38ac247005f4306dd

https://www.jianshu.com/p/a3e162261ab6

上一篇下一篇

猜你喜欢

热点阅读