1.构建步骤:
1.创建实例
2.写接口
3.获取call
4.获得响应
//写好接口,不能忘
public interface BlogService {
@GET("blog/{id}") //这里的{id} 表示是一个变量
Call<ResponseBody> getBlog(/** 这里的id表示的是上面的{id} */@Path("id") int id);
}
public static void main(String[] args) throws IOException {
//构建实例
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://localhost:4567/")
.build();
//获取代理类
BlogService service = retrofit.create(BlogService.class);
//获得call
Call<ResponseBody> call = service.getBlog(2);
// 用法和OkHttp的call如出一辙
// 不同的是如果是Android系统回调方法执行在主线程
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(
Call<ResponseBody> call, Response<ResponseBody> response) {
try {
System.out.println(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
t.printStackTrace();
}
});
}
其实retrofit使用的难点就是接口如何配置,它内部是使用的注解的方法对你的参数进行解析的,最后与url一起构成一个完整的请求路径,所以我们这里将请求的注解分为三类进行学习.
第一类:HTTP请求方法!
这里特别讲解一下HTTP注解,HTTP注解则可以代替以上方法中的任意一个注解,有3个属性:method、path,hasBody.从代码中可以看出,HTTP注解就是其他几个方法的结合.
public interface BlogService {
/**
* method 表示请求的方法,区分大小写,retrofit 不会做处理
* path表示路径
* hasBody表示是否有请求体
*/
//这里的请求方法是要大写的.注意!!!
@HTTP(method = "GET", path = "blog/{id}", hasBody = false)
Call<ResponseBody> getBlog(@Path("id") int id);
}
public static void main(String[] args) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://localhost:4567/")
.build();
BlogService service = retrofit.create(BlogService.class);
Call<ResponseBody> call = service.getBlog(2);
ResponseBodyPrinter.printResponseBody(call);
}
第二类:标记类
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);
// 演示 @FormUrlEncoded 和 @Field
Call<ResponseBody> call1 = service.testFormUrlEncoded1("怪盗kidou", 24);
ResponseBodyPrinter.printResponseBody(call1);
//===================================================
// 演示 @FormUrlEncoded 和 @FieldMap
// 实现的效果与上面想同
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"), "这里是模拟文件的内容");
// 演示 @Multipart 和 @Part
MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", "test.txt", file);
Call<ResponseBody> call3 = service.testFileUpload1(name, age, filePart);
ResponseBodyPrinter.printResponseBody(call3);
//===================================================
// 演示 @Multipart 和 @PartMap
// 实现和上面同样的效果
Map<String, RequestBody> fileUpload2Args = new HashMap<>();
fileUpload2Args.put("name", name);
fileUpload2Args.put("age", age);
//这里并不会被当成文件,因为没有文件名(包含在Content-Disposition请求头中),但上面的 filePart 有
//fileUpload2Args.put("file", file);
Call<ResponseBody> call4 = service.testFileUpload2(fileUpload2Args, filePart); //单独处理文件
ResponseBodyPrinter.printResponseBody(call4);
//===================================================
// 还有一种比较hack的方式可以实现文件上传,
// 上面说过被当成文件上传的必要条件就是 Content-Disposition 请求头中必须要有 filename="xxx" 才会被当成文件
// 所有我们在写文件名的时候可以拼把 filename="XXX" 也拼接上去,
// 即文件名变成 表单键名"; filename="文件名 (两端的引号会自动加,所以这里不加)也可以实现,但是不推荐方式
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);
}
第三类:参数类
注1:{占位符}和PATH尽量只用在URL的path部分,url中的参数使用Query和QueryMap 代替,保证接口定义的简洁
注2:Query、Field和Part这三者都支持数组和实现了Iterable接口的类型,如List,Set等,方便向后台传递数组。
Call<ResponseBody> foo(@Query("ids[]") List<Integer> ids);
//结果:ids[]=0&ids[]=1&ids[]=2
public interface BlogService {
@GET("/headers?showAll=true")
@Headers({"CustomHeader1: customHeaderValue1", "CustomHeader2: customHeaderValue2"})
Call<ResponseBody> testHeader(@Header("CustomHeader3") String customHeaderValue3);
}
public static void main(String[] args) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://localhost:4567/")
.build();
BlogService service = retrofit.create(BlogService.class);
//演示 @Headers 和 @Header
Call<ResponseBody> call1 = service.testHeader("ikidou");
ResponseBodyPrinter.printResponseBody(call1);
}
public interface BlogService {
/**
* 当GET、POST...HTTP等方法中没有设置Url时,则必须使用 {@link Url}提供
* 对于Query和QueryMap,如果不是String(或Map的第二个泛型参数不是String)时
* 会被默认会调用toString转换成String类型
* Url支持的类型有 okhttp3.HttpUrl, String, java.net.URI, android.net.Uri
* {@link retrofit2.http.QueryMap} 用法和{@link retrofit2.http.FieldMap} 用法一样,不再说明
*/
//当有URL注解时,这里的URL就省略了
@GET
Call<ResponseBody> testUrlAndQuery(@Url String url, @Query("showAll") boolean showAll);
}
public static void main(String[] args) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://localhost:4567/")
.build();
BlogService service = retrofit.create(BlogService.class);
//演示 @Headers 和 @Header
Call<ResponseBody> call1 = service.testUrlAndQuery("headers",false);
ResponseBodyPrinter.printResponseBody(call1);
}
3、Converter
在默认情况下Retrofit只支持将HTTP的响应体转换换为ResponseBody,这也是什么我在前面的例子接口的返回值都是Call,但如果响应体只是支持转换为ResponseBody的话何必要引用泛型呢,返回值直接用一个Call就行了嘛,既然支持泛型,那说明泛型参数可以是其它类型的,而Converter就是Retrofit为我们提供用于将ResponseBody转换为我们想要的类型.
记得导包:
compile 'com.squareup.retrofit2:converter-gson:2.0.2'
public interface BlogService {
@GET("blog/{id}")
Call<Result<Blog>> getBlog(@Path("id") int id);
}
public static void main(String[] args) {
Gson gson = new GsonBuilder()
//配置你的Gson
.setDateFormat("yyyy-MM-dd hh:mm:ss")
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://localhost:4567/")
//可以接收自定义的Gson,当然也可以不传
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
BlogService service = retrofit.create(BlogService.class);
Call<Result<Blog>> call = service.getBlog(2);
call.enqueue(new Callback<Result<Blog>>() {
@Override
public void onResponse(Call<Result<Blog>> call, Response<Result<Blog>> response) {
// 已经转换为想要的类型了
Result<Blog> result = response.body();
System.out.println(result);
}
@Override
public void onFailure(Call<Result<Blog>> call, Throwable t) {
t.printStackTrace();
}
});
}
4.RxJava与CallAdapter
CallAdapter可以对Call转换,这样的话Call中的Call可以被替换,而返回值的类型就决定你后续的处理程序逻辑,同样Retrofit提供了多个CallAdapter,这里以RxJava的为例,用Observable代替Call:
引入RxJava支持:
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2'
// 针对rxjava2.x(adapter-rxjava2的版本要 >= 2.2.0)
compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
public interface BlogService {
@GET("/blog")
Observable<Result<List<Blog>>> getBlogs(@Query("page") int page);
/*
「20160608补充」如果需要Header的值,可以把返回值替换为
Observable<Response<Result<List<Blog>>>>
Observable<retrofit2.adapter.rxjava.Result<Result<List<Blog>>>>
*/
}
public static void main(String[] args) {
Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd hh:mm:ss")
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://localhost:4567/")
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
BlogService service = retrofit.create(BlogService.class);
service.getBlogs(1)
.observeOn(Schedulers.io())
.subscribe(new Subscriber<Result<List<Blog>>>() {
@Override
public void onCompleted() {
System.out.println("onCompleted");
}
@Override
public void onError(Throwable e) {
System.err.println("onError");
}
@Override
public void onNext(Result<List<Blog>> blogsResult) {
System.out.println(blogsResult);
}
});
}