开源框架地址:https://github.com/square/retrofit
英文文档官网:http://square.github.io/retrofit/
RxJava框架:https://github.com/ReactiveX/RxJava
okhttp框架:https://github.com/square/okhttp
在对Android 开发中,我们都是从原生的 HttpUrlConnection到经典的 Apache公司的HttpClient,再到对前面这些网络基础框架的封装(比如Volley、AsyncHttpClient等)。Http请求相关开源框架还是很多的,今天我们讲解 Square 公司开源的Retrofit。Square 公司的框架总是一如既往的简洁优雅!Retrofit更是以其简易的接口配置、强大的扩展支持、优雅的代码结构受到大家的追捧。
Retrofit是一个 RESTful 的 HTTP 网络请求框架的封装。注意这里并没有说它是网络请求框架,主要原因在于网络请求的工作并不是Retrofit来完成的。Retrofit2.0 开始内置OkHttp,前者专注于接口的封装,后者专注于网络请求的高效,二者分工协作!
我们的应用程序通过 Retrofit请求网络,实际上是使用Retrofit接口层封装请求参数、Header、Url 等信息,之后由OkHttp完成后续的请求操作,在服务端返回数据之后,OkHttp将原始的结果交给Retrofit,后者根据用户的需求对结果进行解析的过程。
Retrofit的使用就像它的编码风格一样,非常简单,首先你需要在你的 build.gradle 中添加依赖:
接口当中的listRepos方法,就是我们想要访问的api了,在发起请求时,{user}会被替换为方法的第一个参数user。
Retrofit支持的协议包括GET/POST/PUT/DELETE/HEAD/PATCH,当然你也可以直接用HTTP来自定义请求。这些协议均以注解的形式进行配置例如下面GET:
path是绝对路径的形式:
path = "/apath",baseUrl = "http://host:port/a/b"
Url = "http://host:port/apath"
path是相对路径,baseUrl是目录形式:
path = "apath",baseUrl = "http://host:port/a/b/"
Url = "http://host:port/a/b/apath"
path是相对路径,baseUrl是文件形式:
path = "apath",baseUrl = "http://host:port/a/b"
Url = "http://host:port/a/apath"
path是完整的 Url:
path = "http://host:port/aa/apath",baseUrl = "http://host:port/a/b"
Url = "http://host:port/aa/apath"
上面提供的上传文件的方式前后构造了三个对象:File-->RquestBody-->MultipartBody.part看起来其实是非常复杂的。
实际上Retrofit允许我们自己定义入参和返回的类型,不过,如果这些类型比较特别,我们还需要准备相应的 Converter,也正是因为 Converter 的存在,Retrofit在入参和返回类型上表现得非常灵活。该如何做呢,请看下面:
总结上面的技术知识,我们来看完整的请求:
前面我们已经看到 Retrofit为我们构造了一个OkHttpCall,实际上每一个OkHttpCall都对应于一个请求,它主要完成最基础的网络请求,而我们在接口的返回中看到的 Call 默认情况下就是OkHttpCall了,如果我们添加了自定义的callAdapter,那么它就会将OkHttp适配成我们需要的返回值,并返回给我们。
先看下call接口代码:
我们只需要实现 CallAdapter类来提供具体的适配逻辑,并实现相应的Factory,用来将当前的CallAdapter注册到Retrofit当中,并在Factory.get方法中根据类型来返回当前的CallAdapter即可。知道了这些,我们再来看RxJavaCallAdapterFactory:
总结:Retrofit是非常强大的,博客中通过一些示例向大家展示了Retrofit自身强大的功能以及扩展性,就算它本身功能不能满足你的需求,你也可以很容易的进行改造。这就是好的代码设计,能够让其拓展性强大到只有你想不到,没有它做不到的境界!
英文文档官网:http://square.github.io/retrofit/
RxJava框架:https://github.com/ReactiveX/RxJava
okhttp框架:https://github.com/square/okhttp
在对Android 开发中,我们都是从原生的 HttpUrlConnection到经典的 Apache公司的HttpClient,再到对前面这些网络基础框架的封装(比如Volley、AsyncHttpClient等)。Http请求相关开源框架还是很多的,今天我们讲解 Square 公司开源的Retrofit。Square 公司的框架总是一如既往的简洁优雅!Retrofit更是以其简易的接口配置、强大的扩展支持、优雅的代码结构受到大家的追捧。
Retrofit是一个 RESTful 的 HTTP 网络请求框架的封装。注意这里并没有说它是网络请求框架,主要原因在于网络请求的工作并不是Retrofit来完成的。Retrofit2.0 开始内置OkHttp,前者专注于接口的封装,后者专注于网络请求的高效,二者分工协作!
我们的应用程序通过 Retrofit请求网络,实际上是使用Retrofit接口层封装请求参数、Header、Url 等信息,之后由OkHttp完成后续的请求操作,在服务端返回数据之后,OkHttp将原始的结果交给Retrofit,后者根据用户的需求对结果进行解析的过程。
Retrofit的使用就像它的编码风格一样,非常简单,首先你需要在你的 build.gradle 中添加依赖:
compile 'com.squareup.retrofit2:retrofit:2.0.2'
加入我们想要访问 GitHub 的 api 对,那么我们就定义一个接口:
接口当中的listRepos方法,就是我们想要访问的api了,在发起请求时,{user}会被替换为方法的第一个参数user。
Retrofit支持的协议包括GET/POST/PUT/DELETE/HEAD/PATCH,当然你也可以直接用HTTP来自定义请求。这些协议均以注解的形式进行配置例如下面GET:
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
这些注解都有一个参数 value,用来配置其路径,比如示例中的 users/{user}/repos,我们还注意到在构造Retrofit之时我们还传入了一个baseUrl("https://api.github.com/"),请求的完整 Url 就是通过baseUrl与注解的value(下面称 “path“ ) 整合起来的,具体整合的规则如下:path是绝对路径的形式:
path = "/apath",baseUrl = "http://host:port/a/b"
Url = "http://host:port/apath"
path是相对路径,baseUrl是目录形式:
path = "apath",baseUrl = "http://host:port/a/b/"
Url = "http://host:port/a/b/apath"
path是相对路径,baseUrl是文件形式:
path = "apath",baseUrl = "http://host:port/a/b"
Url = "http://host:port/a/apath"
path是完整的 Url:
path = "http://host:port/aa/apath",baseUrl = "http://host:port/a/b"
Url = "http://host:port/aa/apath"
建议采用第二种方式来配置,并尽量使用同一种路径形式。
构造 Retrofit:
Retrofit retrofit = new Retrofit.Builder.baseUrl("https://api.github.com/").build;
GitHubService service = retrofit.create(GitHubService.class);
//service发起请求
Call<List<Repo>> repos = service.listRepos("octocat");
返回的repos其实并不是真正的数据结果,它更像一条指令,你可以在合适的时机去执行它:
Lisg<Repo> data = repos.execute();//同步调用
repos.enqueue(new Callback<List<repo>>(){
@Override
public void onResponse(Call<List<rpo>> call, Response<List<Repo>> response){
List<Repo> data = response.body();
}
@Override
public void onFailure(Call<List<Repo>> call,Throwable t){
t.printStackTrace();
}
});
是不是很简单,其中的接口定义方式就是我们前面有篇博客讲的(浅谈Java回调机制)http://blog.youkuaiyun.com/caihongdao123/article/details/51657840
认识Query & QueryMap
@GET("/list") Call<ResponseBody> list(@Query("page") int page);
Query其实就是 Url 中 ‘?’ 后面的 key-value,比如:这里的 cate=android就是一个Query,而我们在配置它的时候只需要在接口方法中增加一个参数,即可:
interface PrintlnServer{
@GET("/")
Call<String> cate(@Query("cate") String cate);
}
当面临参数很多的情况,我们就采用QueryMap!
认识Field & FieldMap
@FormUrlEncoded
@POST("/")
Call<ResponseBody> example( @Field("name") String name, @Field("occupation") String occupation);
我们用 Field声明了表单的项,这样提交表单就跟普通的函数调用一样简单直接了,表单项不确定个数就试用FieldMap。
认识Part & PartMap
public interface FileUploadService {
@Multipart @POST("upload")
Call<ResponseBody> upload(@Part("description") RequestBody description, @Part MultipartBody.Part file);
}
如果你需要上传文件,和我们前面的做法类似,定义一个接口方法,需要注意的是,这个方法不再有 @FormUrlEncoded这个注解,而换成了@Multipart,后面只需要在参数中增加Part就可以了。也许你会问,这里的Part和Field究竟有什么区别,其实从功能上讲,无非就是客户端向服务端发起请求携带参数的方式不同,并且前者可以携带的参数类型更加丰富,包括数据流。也正是因为这一点,我们可以通过这种方式来上传文件,下面我们就给出这个接口的使用方法:
//先创建Service
FileUploadService service = retrofit.create(FileUploadService.class);
//构建要上传的文件
File file = new File(filename);
RequestBody requestFile = RequestBody.crate(MediaType.parse("application/otcet-stream"),file);
MultipartBody.Part body = MultipartBody.Part.crateFormData("aFile",file.getNmae(),requestFile);
String description = RequestBody.create(MediaType.parse("multipart/form-data"),descriptionString);
Call<ResponseBody> call = service.upload(description,body);
call.enqueue(new Callback<ResponseBody>(){
@Override
public void onResponse(Call<ResponseBody call,Response<ResponseBody> response){
Log.d("success","success");
}
@Override
public void onFailure(Call<ResponseBody> call,Throwable t){
t.printStackTrace();
}
});
如果你需要上传多个文件,就声明多个Part参数,或者试试PartMap!上面提供的上传文件的方式前后构造了三个对象:File-->RquestBody-->MultipartBody.part看起来其实是非常复杂的。
实际上Retrofit允许我们自己定义入参和返回的类型,不过,如果这些类型比较特别,我们还需要准备相应的 Converter,也正是因为 Converter 的存在,Retrofit在入参和返回类型上表现得非常灵活。该如何做呢,请看下面:
public interface FileUploadService {
@Multipart @POST("upload")
Call<ResponseBody> upload(@Part("description") RequestBody description, //注意这里的参数 "aFile" 之前是在创建 MultipartBody.Part 的时候传入的 @Part("aFile")
File file);
}
static class FileRequestBodyConverterFactory extends Converter.Factory {
@Override
public Converter<File, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return new FileRequestBodyConverter;
}
}
static class FileRequestBodyConverter implements Converter<File, RequestBody> {
@Override
public RequestBody convert(File file) throws IOException {
return RequestBody.create(MediaType.parse("application/otcet-stream"), file);
}
}
在创建 Retrofit的时候记得配置上它,这样,我们的文件内容就能上传了:
addConverterFactory(new FileRequestBodyConverterFactory)
注意:Retrofit在选择合适的 Converter 时,主要依赖于需要转换的对象类型,在添加 Converter 时,注意 Converter 支持的类型的包含关系以及其顺序。
总结上面的技术知识,我们来看完整的请求:
前面我们已经看到 Retrofit为我们构造了一个OkHttpCall,实际上每一个OkHttpCall都对应于一个请求,它主要完成最基础的网络请求,而我们在接口的返回中看到的 Call 默认情况下就是OkHttpCall了,如果我们添加了自定义的callAdapter,那么它就会将OkHttp适配成我们需要的返回值,并返回给我们。
先看下call接口代码:
public interface Call<T> extends Cloneable {
//同步发起请求
Response<T> execute throws IOException;
//异步发起请求,结果通过回调返回
void enqueue(Callback<T> callback);
boolean isExecuted;
void cancel;
boolean isCanceled;
Call<T> clone;
//返回原始请求
Request request;
}
接下来执行repos其实就是一个OkHttpCall实例,execute就是要发起网络请求:
Call<List<Repo>> repos = service.listRepos("octocat");
List<Repo> data = repos.execute;
parseResponse主要完成了由okhttp3.Response向retrofit.Response的转换,同时也处理了对原始返回的解析:
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body; //略掉一些代码
try {
//在这里完成了原始 Response 的解析,T 就是我们想要的结果,比如 GitHubService.listRepos 的 List<Repo> T body = serviceMethod.toResponse(catchingBody);
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
// If the underlying source threw an exception, propagate that rather than indicating it was // a runtime exception. catchingBody.throwIfCaught;
throw e;
}
}
处理结果时我想要接入 RxJava,让接口的返回结果改为 Observable:
public interface GitHub {
@GET("/repos/{owner}/{repo}/contributors")
Observable<List<Contributor>> contributors( @Path("owner") String owner, @Path("repo") String repo);
}
只需要提供一个 Adapter,将 OkHttpCall转换为Observable即可。Retrofit提供了相应的 Adapter(RxJavaCallAdapterFactory)我们只需要在构造 Retrofit时,添加它:
addCallAdapterFactory(RxJavaCallAdapterFactory.create)
我们来看看RxJavaCallAdapterFactory是如何工作的:我们只需要实现 CallAdapter类来提供具体的适配逻辑,并实现相应的Factory,用来将当前的CallAdapter注册到Retrofit当中,并在Factory.get方法中根据类型来返回当前的CallAdapter即可。知道了这些,我们再来看RxJavaCallAdapterFactory:
/**
* 只给大家列出来比较重要的代码段
*/
public final class RxJavaCallAdapterFactory extends CallAdapter.Factory{
@Override
public CallAdapger<?> get(Type returnType,Annotation[] annotations,Retrofit retrofit){
//判断returnType是否为RxJava支持的类型
Class<?> rawType = getRawType(returnType);
String CanonicalName = RawType.getCanonicalName();
boolean isSingle = "rx.Single".equals(CanonicalName);
boolean isCompletable = "rx.Completable".equals(CanonicalName);
if(rawType != Observable.class && !isSingle && !isCompletable){
return null;
}
return Adapter;//"获取你需要的Adapter 返回"
}
static final class SimpleCallAdapter implements CallAdapter<Ovservable<?>>{
private final Type responseType;
private final Scheduler scheduler;
SimpleCallAdpter(Type responseType ,Scheduler scheduler){
this.responseType = responseType;
this.scheduler = scheduler;
}
@Override
public Type responseType(){
return responseType;
}
@Override
public <R> Observable<R> adapt(Call<R> call){
//在这里创建需要作为返回值的Observable实例,并持有call实例,所以在Observable.subscribe触发时,call.execute将会被调用
Observable<R> observable = Observable.create(new CallOnSubscribe<>(call)).lift(OperatorMapResponseToBodyOrError.<R>instance());
if(scheduler != null){
return observable.subscribeOn(scheduler);
}
return observable;
}
}
}
总结:Retrofit是非常强大的,博客中通过一些示例向大家展示了Retrofit自身强大的功能以及扩展性,就算它本身功能不能满足你的需求,你也可以很容易的进行改造。这就是好的代码设计,能够让其拓展性强大到只有你想不到,没有它做不到的境界!