Retrofit 的使用介绍
Android
和Java
安全的http请求客户端
版本
去年年底更新到V2.0.0了。V1&&V2
优势
在V1.X.X 的时候:
可以利用接口,方法和注解参数(parameter annotations)来声明式定义一个请求应该如何被创建
@POST("/classes/info") void insertInfoDb(@Body ContactBean bean, Callback<ContactBean> callback); @GET("/classes/info") void getDbInfos( Callback<ResultBean> callback);
可自定义
HttpClient
builder.setClient(new AndroidApacheClient()) .setClient(new UrlConnectionClient()) .setClient(new ApacheClient()) .setClient(new OkClient()) .setClient(new CustomClient())
可自定义
序列化机制
builder.setConverter(new GsonConverter()); builder.setConverter(new JacksonConverter()); builder.setConverter(new ProtoConverter()); builder.setConverter(new WireConverter()); builder.setConverter(new SimpleXMLConverter()); builder.setConverter(new CustomConverter());
支持
Rxjava
@GET("/news/latest") Observable<DailyStories> getLatestDailyStories();
支持同步异步请求
@GET("/news/latest") void getLatestDailyStories(Callback<DailyStories> callback); @GET("/news/latest") DailyStories getsLatestDailyStories();
注解的相关类型
- 请求方法
每个方法必须有一个HTTP请求注释提供方法和相对URL。有五个内置注释:GET、POST、PUT、DELETE和头部。资源的相对URL中指定注释。
URL操作
请求URL使用替换块和参数可以动态更新方法。是一个字母数字字符串替换块{和}包围。相应的参数必须与@path注释使用相同的字符串。
例如:
@GET("/start-image/{width}*{height}") void getStartImage(@Path("width") int width, @Path("height") int height, Callback<StartImage> callback);
查询参数也可以被添加。
@GET("group/{id}/users") Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);
如果参数特别多,那么就是用map,咱们不就是这个玩意儿嘛!
@GET("group/{id}/users") Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);
请求体
可以指定一个对象作为HTTP请求体
@Body
注释。
序列化功能也是可替换的。默认是用的 GSON。@POST("/classes/info") void insertInfoDb(@Body ContactBean bean, Callback<ContactBean> callback);
表单编码
如果需要发送表单编码,那么就需要是用
@FormUrlEncoded
。每个键-值对注释@Field
包含名称和对象提供的值。@FormUrlEncoded @POST("user/edit")
文件上传?
@Multipart @PUT("user/photo") Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);
头操作
你可以使用
@Headers
注释静态头的方法。
这里需要注意下——头不会互相覆盖。具有相同名称的所有头文件都包括在请求。@Headers({ "Accept: application/vnd.github.v3.full+json", "User-Agent: Retrofit-Sample-App" }) @GET("users/{username}") Call<User> getUser(@Path("username") String username);
如果我们在方法中动态再使用@Header的的话,@Header必须提供相应的参数。如果该值为null,头就会被忽略掉。然后会通过
toString()
获取它的相关值并使用。如果你需要在每一个方法中都要添加相关的header,那么这种方法就比较繁琐了。可以使用
OkHttp interceptor
,在创建的时候直接添加相关参数@GET("user") Call<User> getUser(@Header("Authorization") String authorization)
以上就是Retrofit
提供的相关注解了。
举个例子
定义一个接口:
public interface BombApiService { @POST("/classes/info") void insertInfoDb(@Body ContactBean bean, Callback<ContactBean> callback); @GET("/classes/info") void getDbInfos( Callback<ResultBean> callback); @GET("/classes/info") Observable<ResultBean> getDbInfos(); @POST("/batch") void insertDbInfos(@Body BatchBean bean,Callback<List<BatchResultBean>> callback); }
获取这个接口的实例化对象:
public static BombApiService getBombApiService() { return new RestAdapter.Builder() .setEndpoint(BOMB_API)//hosturl .setConverter(new GsonConverter()) .setLogLevel(BuildConfig.DEBUG ? RestAdapter.LogLevel.FULL : RestAdapter.LogLevel.NONE)//是否打印日志 .setRequestInterceptor(new RequestInterceptor() { @Override public void intercept(RequestFacade request) { request.addHeader("Content-Type", "application/json"); request.addHeader("X-Bmob-Application-Id", APPLICATION_ID); request.addHeader("X-Bmob-REST-API-Key", API_KEY); } })//这里就是使用Interceptor来添加相关的header,但是这个是V1里面的相关写法。V2.0请往下看。 .build() .create(BombApiService.class); }
相关的请求方法:
getBombApiService().getDbInfos()
酱紫,就已经成功调取相关的api了!!
V1.0的一些不足
如果你想要操作某次请求返回的数据,比如说返回的 Header 部分或者 URL,你又同时想要操作序列化后的数据部分。
同一个方法同步异步的定义必须分开定义两次。
声明式 API 需要传入一个 Date,但是一个 Date 会有多种不同的格式表示。有的接口可能需要一个字符串,有的可能需要一个分隔开的日期表示(尤其是那些比日期要复杂很多的对象,可能会有更多的表示方法。
一中RestAdapter
只能绑定一个Converter
对象。
url的拼接相对死板:@POST(“/classes/info”),这里必须以/开头
V2.0.0的相关新特性
1、Call方法的使用,引入了Call方法。
@GET("/some/proto/endpoint") Call<SomeProtoResponse> someProtoEndpoint();
2、同步异步同一个方法的支持(基于call方法)引入call方法之后,使用完全和OkHttp的Call方法是一致的。
同步调用:我们可以直接调用它的 execute 方法,但是得留意一下,这个方法只能调用一次。重复调用会抛出异常
response = call.execute(); // This will throw IllegalStateException: response = call.execute();
异步调用:我们可以直接调用它的 enqueue 方法
call.enqueue(new Callback<List<Contributor>>() { @Override void onResponse(/* ... */) { // ... } @Override void onFailure(Throwable t) { // ... } });
事实取消相关请求:当你将一些异步请求压入队列后,甚至你在执行同步请求的时候,你可以随时调用
cancel()
方法来取消请求。Call<List<Contributor>> call = gitHubService.repoContributors("square", "retrofit"); call.enqueue( ); // or... call.execute(); // later... call.cancel(); Parameterize
3、参数化的响应对象&支持对header和序列化数据的同时操作
在这个新的
Response
对象增加了曾经一直被我们忽略掉的重要元数据:响应码(the reponse code),响应消息(the response message),以及读取相应头(headers)。
同时还提供了一个很方便的函数来帮助你判断请求是否成功完成,其实就是检查了下响应码是不是 200。然后就拿到了响应的 body 部分,另外有一个单独的方法获取 error body。基本上就是出现一个返回码,然后调用相对应的函数的。只有当响应成功以后,我们会去做反序列化操作,然后将反序列化的结果放到 body 回调中去。如果出现了返回了网络成功响应(返回码:200)却最终返回 false 的情况,我们实际上是无法判断返回到底是什么的,只能将ResponseBody
(简单封装的了 content-type,length,以及 raw body部分) 类型交给你去处理。Call<List<Contributor>> call = gitHubService.repoContributors("square", "retrofit"); Response<List<Contributor>> response = call.execute(); response.isSuccess()//判断是否成功 response.headers()//成功获取相关的header response.errorBody//请求异常的相关封装
4、动态 URL Parameter
Retrofit 2.0 有了新的 标注:@Url ,允许你直接传入一个请求的 URL。
@GET Call<List<Contributor>> repoContributorsPaginate( @Url String url);
5、OkHttp 提供支持,其他不在支持
如果需要统一添加header,那么就是直接在这个
client
里面添加对应的RequestInterceptor
.okHttpClient.interceptors().add(requestInterceptor); Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com") .client(client) .build();
6、Adapter的创建
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com") .build();
7、不同Converter的同时支持
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com") .addConverterFactory(ProtoConverterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build();
这里要注意
Converter
的添加顺序,因为Gson
的Convertor
是所有的都能处理的,所以这个一定要放在最后面。有点儿像java的捕获异常,子异常要放在上面,父异常要放在最下面。8、相关路径写法的改变
现在,让我们来看一下Retrofit
的类型是如何替代REST adapter
类型的,以及如何初始化。原来的方法叫做endpoint()
, 不过现在我们称之为baseUrl()
,baseUrl
就是你所请求的 Server 的 URL,下面是一个请求 GitHub Api 的例子:Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com") .build(); interface GitHubService { @GET("/repos/{owner}/{repo}/contributors") Call<List<Contributor>> repoContributors( @Path("owner") String owner, @Path("repo") String repo); } GitHubService gitHubService = retrofit.create(GitHubService.class); 其实url是这样的:https://api.github.com/repos/square/retrofit/contributors Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/v3/") .build(); interface GitHubService { @GET("repos/{owner}/{repo}/contributors") Call<List<Contributor>> repoContributors( @Path("owner") String owner, @Path("repo") String repo); } 其实url是这样的: https://api.github.com/v3/repos/square/retrofit/contributors
意思就是在V2.0之后,我们不需要刻意的使用/开头的url了,如果这样开头,会被认为 是一个绝对路径。
所以现在我们的BaseUrl
应该是以”/”结束的,然后定义get等方法时直接拼接对应的url,而不用刻意的使用”/”开头!!。9、Rxjava的相关支持
CallAdapterFactory
是一个知道如何将call
实例转换成其他类型的工厂类。目前,我们只有RxJava
的类型,也就是将Call
类型转换成Observable
类型。Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com") .addConverterFactory(ProtoConverterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build();
其中 1 8 9是对应V1版本在初始化的方法上差异比较大的地方吧!!
报错
引入Retrofit2.0之后系统提示GsonConverterFactory
或者RxJavaCallAdapterFactory
等找不到???
不要惊慌,这些都要分别引入的!!而且注意对应的包名确定一致,bate版本使用bate版本相关的Factory
compile 'com.squareup.retrofit2:retrofit:2.0.0'
compile 'com.squareup.retrofit2:converter-gson:2.0.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0'
或者
compile 'com.squareup.retrofit:retrofit:2.0.0-beta2'
compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'
compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta2'