初识Retrofit2
文章首发自本人语雀,转载请注明出处(本文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