美文网首页
初识Retrofit2

初识Retrofit2

作者: 做梦枯岛醒 | 来源:发表于2019-11-26 10:33 被阅读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

    相关文章

      网友评论

          本文标题:初识Retrofit2

          本文链接:https://www.haomeiwen.com/subject/dafswctx.html