导包
module:
//retrofit 和Gson转换,由于retrofit是基于okhttp所以,还需要添加okhttp的依赖
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.squareup.okhttp3:okhttp:3.4.1'
基本使用+Gson转换
api:
https://api.github.com/users/baiiu
Json解析的model类,这里没有写出get set方法:
public class User {
private String login;
private int id;
private String avatar_url;
private String gravatar_id;
private String url;
private String html_url;
private String followers_url;
private String following_url;
private String gists_url;
private String starred_url;
private String subscriptions_url;
private String organizations_url;
private String repos_url;
private String events_url;
private String received_events_url;
private String type;
private boolean site_admin;
private String name;
private Object company;
private String blog;
private String location;
private Object email;
private boolean hireable;
private String bio;
private int public_repos;
private int public_gists;
private int followers;
private int following;
private String created_at;
private String updated_at;
}
定义接口
public interface GitHubAPI {
@GET("users/{user}")
Call<User> userInfo(@Path("user") String user);
}
使用方法
//以okhttp为client对象
OkHttpClient client = new OkHttpClient()
//创建retrofit对象
Retrofit retrofit = new Retrofit.Builder()
//设置OKHttpClient
.client(client)
//设置baseUrl,注意,baseUrl必须以后缀"/"结尾,否则报错
.baseUrl("https://api.github.com/")
//添加Gson转换器,将接口返回的数据转换为jsonmodel对象
.addConverterFactory(GsonConverterFactory.create())
.build()
//API对象,表示不同的api
GitHubAPI gitHubAPI = retrofit.create(GitHubAPI.class)
//创建call对象,表示请求
<User> userCall = gitHubAPI.userInfo("baiiu")
//异步请求,也就是在不同线程
userCall.enqueue(new Callback<User>() {
@Override
public void onResponse(Call<User> call, Response<User> response) {
User body = response.body()
Log.v("meee", getClass() + ":\n" + "body:" + body.toString())
//但callback是在主线程中的
Log.v("meee",getClass()+":\n"+"线程:"+Thread.currentThread().getName())
}
@Override
public void onFailure(Call<User> call, Throwable t) {
if (call.isCanceled()) {
Log.v("meee",getClass()+":\n"+"请求被终止")
} else {
Log.v("meee",getClass()+":\n"+""+t.getMessage())
}
}
})
//同步请求,与发起请求在同一个线程
//因为每一个call对象只能被使用一次,而userCall在上面已经被使用过了,所以克隆一个请求
Call<User> userCallClone = userCall.clone()
Response<User> response = userCallClone.execute()
response.body().toString()
不使用Gson转换
public interface GitHubAPI {
@GET
Call<ResponseBody> raw(@Url String url);
}
完整网址的请求
因为总有api是不同的,有时候为其生成独立api很麻烦,所以我们可以直接传入全路径网址来进行请求
api接口中添加一个传入全路径url的接口方法
public interface GitHubAPI {
@GET("users/{user}")
Call<User> userInfo(@Path("user") String user);
@GET
Call<User> url(@Url String url);
}
Call<User> url = gitHubAPI.url("https://api.github.com/users/baiiu");
url.enqueue(new Callback<User>() {
@Override
public void onResponse(Call<User> call, Response<User> response) {
User body = response.body();
Log.v("meee", getClass() + ":\n" + "url:" + body.toString());
}
@Override
public void onFailure(Call<User> call, Throwable t) {
}
});
Post请求
public interface MallAPI {
@FormUrlEncoded
@POST("login")
Call<ResponseBody> login(@Field("username")String username, @Field("pwd") String pwd);
}
MallAPI mallAPI = retrofit2.create(MallAPI.class);
Call<ResponseBody> loginCall = mallAPI.login("123","123456");
loginCall.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
ResponseBody body = response.body();
try {
Log.v("meee",getClass()+":\n"+""+body.string());
} catch (IOException e) {
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
});
也可以使用属性map来传递参数
public interface MallAPI {
@FormUrlEncoded
@POST("login")
Call<ResponseBody> login(@FieldMap Map<String,String>map);
}
Call<ResponseBody> loginCall = mallAPI.login(map);
添加请求头
public interface GitHubAPI {
@Headers({
"Accept: application/vnd.github.v3.full+json",
"User-Agent: Retrofit-Sample-App"
})
@GET
Call<User> urlWithHeaderStatic(@Url String url);
Call<User> urlWithHeaderDynamic(@Url String url,@Header("Authorization") String authorization);
}
Interceptors的使用
导包,因为retrofit底层是使用okhttp,所以使用okhttp的Interceptors
//okhttp拦截请求日志
compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'
public final class UserAgentInterceptor implements Interceptor {
private static final String USER_AGENT_HEADER_NAME = "User-Agent";
private final String userAgentHeaderValue;
public UserAgentInterceptor(String userAgentHeaderValue) {
this.userAgentHeaderValue = userAgentHeaderValue;
}
@Override public okhttp3.Response intercept(Chain chain) throws IOException {
final Request originalRequest = chain.request();
final Request requestWithUserAgent = originalRequest.newBuilder()
.removeHeader(USER_AGENT_HEADER_NAME)
.addHeader(USER_AGENT_HEADER_NAME, userAgentHeaderValue)
.build();
return chain.proceed(requestWithUserAgent);
}
}
//在创建client时,传入创建的Interceptor
OkHttpClient okHttpClient = new OkHttpClient.Builder()
//添加UA
.addInterceptor(new UserAgentInterceptor("header"))
//失败重连
.retryOnConnectionFailure(true)
//time out
.readTimeout(10, TimeUnit.SECONDS)
.connectTimeout(10, TimeUnit.SECONDS)
.build()
混淆规则
/*
* -dontwarn retrofit2.**
* -keep class retrofit2.** { *; }
* -keepattributes Signature
* -keepattributes Exceptions
* */
注解详解
http类:
![注解](https://i-blog.csdnimg.cn/blog_migrate/baf72f2b8afcc41887aa96891ca5ca28.webp?x-image-process=image/format,png)
其中http注解可以代替以上7中的http标准注解
public interface BlogService {
/**
* method 表示请求的方法,区分大小写
* path表示路径
* hasBody表示是否有请求体
*/
@HTTP(method = "GET", path = "blog/{id}", hasBody = false)
Call<ResponseBody> getBlog(@Path("id") int id);
}
标记类
![这里写图片描述](https://i-blog.csdnimg.cn/blog_migrate/12c0190e3e36a73e1cb2d0612a2f5469.webp?x-image-process=image/format,png)
public interface BlogService {
/**
* {@link FormUrlEncoded} 表明是一个表单格式的请求(Content-Type:application/x-www-form-urlencoded)
* <code>Field("username")</code> 表示将后面的 <code>String name</code> 中name的取值作为 username 的值
*/
@POST("/form")
@FormUrlEncoded
Call<ResponseBody> testFormUrlEncoded1(@Field("username") String name, @Field("age") int age);
/**
* Map的key作为表单的键
*/
@POST("/form")
@FormUrlEncoded
Call<ResponseBody> testFormUrlEncoded2(@FieldMap Map<String, Object> map);
/**
* {@link Part} 后面支持三种类型,{@link RequestBody}、{@link okhttp3.MultipartBody.Part} 、任意类型
* 除 {@link okhttp3.MultipartBody.Part} 以外,其它类型都必须带上表单字段({@link okhttp3.MultipartBody.Part} 中已经包含了表单字段的信息),
*/
@POST("/form")
@Multipart
Call<ResponseBody> testFileUpload1(@Part("name") RequestBody name, @Part("age") RequestBody age, @Part MultipartBody.Part file);
/**
* PartMap 注解支持一个Map作为参数,支持 {@link RequestBody } 类型,
* 如果有其它的类型,会被{@link retrofit2.Converter}转换,如后面会介绍的 使用{@link com.google.gson.Gson} 的 {@link retrofit2.converter.gson.GsonRequestBodyConverter}
* 所以{@link MultipartBody.Part} 就不适用了,所以文件只能用<b> @Part MultipartBody.Part </b>
*/
@POST("/form")
@Multipart
Call<ResponseBody> testFileUpload2(@PartMap Map<String, RequestBody> args, @Part MultipartBody.Part file);
@POST("/form")
@Multipart
Call<ResponseBody> testFileUpload3(@PartMap Map<String, RequestBody> args);
}
public static void main(String[] args) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://localhost:4567/")
.build();
BlogService service = retrofit.create(BlogService.class);
Call<ResponseBody> call1 = service.testFormUrlEncoded1("怪盗kidou", 24);
ResponseBodyPrinter.printResponseBody(call1);
Map<String, Object> map = new HashMap<>();
map.put("username", "怪盗kidou");
map.put("age", 24);
Call<ResponseBody> call2 = service.testFormUrlEncoded2(map);
ResponseBodyPrinter.printResponseBody(call2);
MediaType textType = MediaType.parse("text/plain");
RequestBody name = RequestBody.create(textType, "怪盗kidou");
RequestBody age = RequestBody.create(textType, "24");
RequestBody file = RequestBody.create(MediaType.parse("application/octet-stream"), "这里是模拟文件的内容");
MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", "test.txt", file);
Call<ResponseBody> call3 = service.testFileUpload1(name, age, filePart);
ResponseBodyPrinter.printResponseBody(call3);
Map<String, RequestBody> fileUpload2Args = new HashMap<>();
fileUpload2Args.put("name", name);
fileUpload2Args.put("age", age);
Call<ResponseBody> call4 = service.testFileUpload2(fileUpload2Args, filePart);
ResponseBodyPrinter.printResponseBody(call4);
Map<String, RequestBody> fileUpload3Args = new HashMap<>();
fileUpload3Args.put("name",name);
fileUpload3Args.put("age",age);
fileUpload3Args.put("file\"; filename=\"test.txt",file);
Call<ResponseBody> testFileUpload3 = service.testFileUpload3(fileUpload3Args);
ResponseBodyPrinter.printResponseBody(testFileUpload3);
}
参数类
![这里写图片描述](https://i-blog.csdnimg.cn/blog_migrate/602eebeb85ef3a5ee279170022211bc4.webp?x-image-process=image/format,png)
{占位符}和PATH尽量只用在URL的path部分,url中的参数使用Query和QueryMap 代替,保证接口定义的简洁
Query、Field和Part这三者都支持数组和实现了Iterable接口的类型,如List,Set等,方便向后台传递数组。
demo:
Call<ResponseBody> foo(@Query("ids[]") List<Integer> ids);
//结果:ids[]=0&ids[]=1&ids[]=2
RxJava和CallAdapter
Converter是对于Call<T>中T的转换,而CallAdapter则可以对Call转换;
导包
compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
public interface MallAPI {
@FormUrlEncoded
@POST("login")
Observable<ResponseBody> login2(@FieldMap Map<String, String> map);
}
Retrofit retrofit3 = new Retrofit.Builder()
.client(client)
.baseUrl("url")
.addConverterFactory(GsonConverterFactory.create())
//添加calladapter支持
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
MallAPI mallAPI2 = retrofit3.create(MallAPI.class)
//RxJava走起
Observable<ResponseBody> responseBodyObservable = mallAPI2.login2(map)
responseBodyObservable
.subscribeOn(Schedulers.newThread())
.subscribe(new Consumer<ResponseBody>() {
@Override
public void accept(ResponseBody responseBody) throws Exception {
Log.v("meee",getClass()+":\n"+"responseBody:"+responseBody.string())
}
})
Retrofit的Url组合规则
公式:BaseUrl + URL有关的注解中的内容 = 组合结果
"http://localhost:4567/path/to/other/" + "/post" = "http://localhost:4567/post"
"http://localhost:4567/path/to/other/" + "post" = "http://localhost:4567/path/to/other/post"
"http://localhost:4567/post" + "https://github.com/ikidou"="https://github.com/ikidou"
小技巧
在取得结果的地方可以添加
public interface MallAPI {
@FormUrlEncoded
@POST("login")
Observable<User> login2(@FieldMap Map<String, String> map);
}
可以改成
public interface MallAPI {
@FormUrlEncoded
@POST("login")
Observable<Response<User>> login2(@FieldMap Map<String, String> map);
}