Retrofit详解(上)
之前写过一篇文章volley-retrofit-okhttp之我们该如何选择网路框架来分析Volley与Retrofit之间的区别。之前一直用Volley比较多。但是随着Rx系列的走红,目前越来越多的项目使用RxJava+Retrofit这一黄金组合。而且Retrofit使用注解的方式比较方便以及2.x版本的提示让Retrofit更加完善,今天简单的来学习记录下。
- 有关更多
Volley的知识请查看Volley源码分析 - 有关注解更多的知识请查看注解使用
- 有关更多
RxJava的介绍请查看Rx详解系列
简介
A type-safe HTTP client for Android and Java
type-safe是什么鬼?
类型安全代码指访问被授权可以访问的内存位置。例如,类型安全代码不能从其他对象的私有字段读取值。它只从定义完善的允许方式访问类型才能读取。
使用
Gradle中集成:
compile 'com.squareup.retrofit2:retrofit:2.1.0'
Retrofit 2底层默认使用自家兄弟OKHttp作为网络层,并且在它上面进行构建。所以不需要在想1.x版本那样在
项目中显式的定义OkHttp依赖。所以Retrofit与OkHttp的关系是后者专注与网络请求的高效优化,而前者专注于接口的封装和调用管理等。

当然你还需要在清单文件中添加网络请求的权限:
<uses-permission android:name="android.permission.INTERNET"/>
我们就以Github获取个人仓库的api来进行举例测试:
https://api.github.com/users/{user}/repos
Retrofit会将你的api封装成Java接口public interface GitHubService { @GET("users/{user}/repos") Call<List<Repo>> listRepos(@Path("user") String user); }Retrofit类会生成一个GitHubService接口的实现类:Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .build(); GitHubService service = retrofit.create(GitHubService.class);从创建的
GithubService类返回的每个Call对象调用后都可以创建一个同步或异步的网络请求:Call<List<Repo>> repos = service.listRepos("CharonChui");上面返回的
Call其实并不是真正的数据结果,它更像一条指令,你需要执行它:// 同步调用 List<Repo> data = repos.execute(); // 异步调用 repos.enqueue(new Callback<List<Repo>>() { @Override public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) { List<Repo> data = response.body(); Log.i("@@@", "data size : " + (data == null ? "null" : data.size() + "")); } @Override public void onFailure(Call<List<Repo>> call, Throwable t) { } });那如何取消请求呢?
repos.cancel();
上面这一部分代码,你要是拷贝运行后是运行不了的。
当然了,因为木有Repo对象。但是添加Repo对象也是运行不了的。会报错。
Process: com.charon.retrofitdemo, PID: 7229
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.charon.retrofitdemo/com.charon.retrofitdemo.MainActivity}: java.lang.IllegalArgumentException: Unable to create converter for java.util.List<com.charon.retrofitdemo.Repo>
for method GitHubService.listRepos
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2281)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2331)
at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3974)
at android.app.ActivityThread.access$1100(ActivityThread.java:143)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1250)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5291)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:849)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:665)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.IllegalArgumentException: Unable to create converter for java.util.List<com.charon.retrofitdemo.Repo>
for method GitHubService.listRepos
at retrofit2.ServiceMethod$Builder.methodError(ServiceMethod.java:720)
at retrofit2.ServiceMethod$Builder.createResponseConverter(ServiceMethod.java:706)
at retrofit2.ServiceMethod$Builder.build(ServiceMethod.java:167)
at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:166)
at retrofit2.Retrofit$1.invoke(Retrofit.java:145)
at $Proxy0.listRepos(Native Method)
at com.charon.retrofitdemo.MainActivity$override.onCreate(MainActivity.java:29)
at com.charon.retrofitdemo.MainActivity$override.access$dispatch(MainActivity.java)
at com.charon.retrofitdemo.MainActivity.onCreate(MainActivity.java:0)
at android.app.Activity.performCreate(Activity.java:5304)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1090)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2245)
... 12 more
Caused by: java.lang.IllegalArgumentException: Could not locate ResponseBody converter for java.util.List<com.charon.retrofitdemo.Repo>.
Tried:
这是什么鬼?难道官方给的示例代码有问题? 从log上面我们能看出来是返回的数据结构不匹配导致的,返回的是ResponseBody的转换器无法转换为List<Repo>。
Retrofit是一个将API接口转换成回调对象的类,默认情况下Retrofit会根绝平台提供一些默认的配置,但是它是支持配置的。
Converters(解析数据)
默认情况下,Retrofit只能将HTTP响应体反序列化为OkHttp的ResponseBody类型。并且通过@Body也只能接受RequestBody类型。
可以通过添加转换器来支持其他类型。它提供了6中类似的序列化类库来方便进行使用:
- Gson: com.squareup.retrofit2:converter-gson
- Jackson: com.squareup.retrofit2:converter-jackson
- Moshi: com.squareup.retrofit2:converter-moshi
- Protobuf: com.squareup.retrofit2:converter-protobuf
- Wire: com.squareup.retrofit2:converter-wire
- Simple XML: com.squareup.retrofit2:converter-simplexml
- Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars
我们这里通过Gson为例,讲解一下如何使用,首先需要在gradle文件中配置对应的支持模块。
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
下面是一个通过使用GsonConverterFactory类来指定GitHubService接口使用Gson来解析结果的配置。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.addConverterFactory(GsonConverterFactory.create())
.build();
GitHubService service = retrofit.create(GitHubService.class);
经过这些改造,就可以了,贴一下完整的代码:
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
GitHubService service = retrofit.create(GitHubService.class);
Call<List<Repo>> call= service.listRepos("CharonChui");
call.enqueue(new Callback<List<Repo>>() {
@Override
public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
List<Repo> body = response.body();
Log.i("@@@", "call " + body.size());
}
@Override
public void onFailure(Call<List<Repo>> call, Throwable t) {
}
});
}
}
运行上面的代码,log会打印出12-14 14:43:38.900 21509-21509/com.charon.retrofitdemo I/@@@: call 26
到这里,我们入门的Hello World就完成了。
Retrofit支持的协议包括GET/POST/PUT/DELETE/HEAD/PATCH,当然你也可以直接用HTTP 来自定义请求。这些协议均以注解的形式进行配置,比如我们已经见过GET的用法.
我们发现在Retrofit创建的时候需要传入一个baseUrl(https://api.github.com/),在GitHubService中会通过注解设置@GET("users/{user}/repos"),请求的完整Url就是通过baseUrl与注解的value也就是path路径结合起来组成。虽然提供了多种规则,但是统一使用下面这一种是最好的。
通过注解的value指定的是相对路径,baseUrl是目录形式:
path = "users/CharonChui/repos",baseUrl = "https://api.github.com/"
Url = "https://api.github.com/users/CharonChui/repos"
上面介绍的例子中使用的是@Path。
@Query及@QueryMap
假设我们有一个分页查询的功能:
GET("/list")
Call<ResponseBody> list(@Query("page") int page);
这就相当于https://api.github.com/list?page=1这种。Query其实就是Url中?后面的k-v。而QueryMap就是多个查询条件。
@Field及@FieldMap
用Post的场景相对比较多,绝大多数的服务端接口要做加密、校验等。所以使用Post提交表单的场景就会很多。
@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);
当然FieldMap就是多个的版本了。
@Part及@PartMap
上传文件时使用。
public interface FileUploadService {
@Multipart
@PUT("user/photo")
Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);
}
@Headers
可以通过@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注解来动态更新
@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)
如果传的值是null,该请求头将会被忽略,否则将会使用该值得toString。
RxJava+Retrofit
Retrofit如何与RxJava结合使用呢?
添加依赖
compile 'com.squareup.retrofit2:retrofit:2.1.0' compile 'com.squareup.retrofit2:converter-gson:2.1.0'// 支持gson compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'// 支持rxjava // rxjava part compile 'io.reactivex:rxandroid:1.2.1' compile 'io.reactivex:rxjava:1.2.3'修改
Retrofit的配置,让其支持RxJavaRetrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // 支持RxJava .build();修改
GitHubService,将返回值改为Observable,而不是Call。public interface GitHubService { @GET("users/{user}/repos") Observable<List<Repo>> listRepos(@Path("user") String user); }执行部分
GitHubService service = retrofit.create(GitHubService.class); service.listRepos("CharonChui") .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<List<Repo>>() { @Override public void onCompleted() { Log.i("@@@", "onCompleted"); } @Override public void onError(Throwable e) { Log.i("@@@", "onError : " + e.toString()); } @Override public void onNext(List<Repo> repos) { Log.i("@@@", "onNext : " + repos.size()); Toast.makeText(MainActivity.this, "size : " + repos.size(), Toast.LENGTH_SHORT).show(); } });RxJava+Retrofit形式的时候,Retrofit把请求封装进Observable在请求结束后调用onNext()或在请求失败后调用onError()。
Proguard配置
如果项目中使用了Proguard,你需要添加如下配置:
# Platform calls Class.forName on types which do not exist on Android to determine platform.
-dontnote retrofit2.Platform
# Platform used when running on RoboVM on iOS. Will not be used at runtime.
-dontnote retrofit2.Platform$IOS$MainThreadExecutor
# Platform used when running on Java 8 VMs. Will not be used at runtime.
-dontwarn retrofit2.Platform$Java8
# Retain generic type information for use by reflection by converters and adapters.
-keepattributes Signature
# Retain declared checked exceptions for use by a Proxy instance.
-keepattributes Exceptions
你的star是我的动力!!!
本文介绍了Retrofit的基本使用方法,包括集成、配置、请求发送及结果处理,并演示了如何结合RxJava进行异步操作。
1021

被折叠的 条评论
为什么被折叠?



