Retrofit基本使用
Retrofit是什么
官网介绍是A type-safe HTTP client for Android and Java,是一个 RESTful 的 HTTP 网络请求框架的封装,但网络请求不是Retrofit来完成的,它只是封装了请求参数、Header、Url、返回结果处理等信息,而请求是由OkHttp3来完成的。
Retrofit基本使用
-
导包
//网络请求相关 implementation "com.squareup.retrofit2:retrofit:$rootProject.retrofitVersion" implementation "com.squareup.retrofit2:retrofit-mock:$rootProject.retrofitVersion" implementation "com.squareup.retrofit2:converter-gson:$rootProject.retrofitVersion" implementation 'com.squareup.okhttp3:logging-interceptor:3.5.0' implementation "com.squareup.retrofit2:converter-scalars:$rootProject.retrofitVersion" implementation "com.squareup.retrofit2:adapter-rxjava2:$rootProject.retrofitVersion" implementation "com.squareup.retrofit2:converter-gson:$rootProject.retrofitVersion"
-
定义一个HTTP API接口类
interface TestApi { @GET("project/tree/json") Call<ProjectBean> getProject(); }
-
使用Retrofifit类生成TestApi接口实现
Retrofit retrofit=new Retrofit.Builder()//建造者模式 .baseUrl("https://www.wanandroid.com/") .addConverterFactory(GsonConverterFactory.create()) .build(); TestApi testApi=retrofit.create(TestApi.class);//代理实例
-
发送HTTP请求,返回Response可以同步或者异步处理
Call<ProjectBean> call=testApi.getProject();//获取具体的某个业务 //同步请求 Response<ProjectBean> response = call.execute(); ProjectBean projectBean = response.body(); //异步请求 call.enqueue(new Callback<ProjectBean>(){ @Override public void onResponse(final Call<ProjectBean> call,final Response<ProjectBean> response){ } @Override public void onFailure(final Call<ProjectBean> call,final Throwable t){ } });
注解分类解析
package retrofit2.http
请求方法类
序号 | 名称 | 说明 |
---|---|---|
1 | GET | get请求 |
2 | POST | post请求 |
3 | PUT | put请求 |
4 | DELETE | delete请求 |
5 | PATCH | patch请求,该请求是对put请求的补充,用于更新局部资源 |
6 | HEAD | head请求 |
7 | OPTIONS | option请求 |
8 | HTTP | 通用注解,可以替换以上所有的注解,其拥有method, path, hasBody 三个属性 |
序号 1 ~ 7
-
分别对应 HTTP 的请求方法;
-
接收一个字符串表示接口 path ,与 baseUrl 组成完整的 Url;
@GET("project/tree/json") Call<ProjectBean> getProject1();
-
可以不指定,结合 @Url 注解使用;
-
url 中可以使用变量,如 {id} ,并使用 @Path(“id”) 注解为 {id} 提供值。
@GET("project/{id}/list") Call<ResponseBody> exmaple5(@Path("id") int id):
序号 8
可用于替代以上 7 个,及其他扩展方法;
有 3 个属性:method、path、hasBody、 举个例子
@HTTP(method = "get", path = "project/tree/json",hasBody = false)
Call<ProjectBean> getProject2();
参数类
名称 | 分类 | 备注 |
---|---|---|
Headers | 作用于方法 | 用于添加固定请求头,可以同时添加多个。通过该注解添加的请求 头不会相互覆盖,而是共同存在 |
Header | 作用于方 法参数(形 参) | 作为方法的参数传入,用于添加不固定值的Header,该注解会更 新已有的请求头 |
Body | 请求参数 | 多用于post请求发送非表单数据,比如想要以post方式传递json格 式数据 |
Field | 多用于post请求中表单字段,Filed和FieldMap需要 FormUrlEncoded结合使用 | |
FieldMap | 表单字段,与Field、FormUrlEncoded配合;接受Map< String,String>类型,非String 类型会调用 toString()方法 | |
Part | 用于表单字段,Part和PartMap与Multipart注解结合使用,适合文件 上传的情况 | |
PartMap | 表单字段,与Part配合,适合文件上传情况;默认接受Map< String, RequestBody>类型,非RequestBody会通过Converter转换 | |
HeaderMap | 用于URL,添加请求头 | |
Path | 用于url中的占位符 | |
Query | 用于Get中指定参数 | |
QueryMap | 和Query使用类似 | |
Url | 指定请求路径 |
Headers
使用 @Headers 注解设置固定的请求头,所有请求头不会相互覆盖,即使名字相同。
@Headers({ "Accept: application/vnd.github.v3.full+json","User-Agent: Retrofit-Sample-App"}) @GET("project/{username}")
Call<ProjectBean> getMsg2(@Path("username") String username);
Header
使用 @Header 注解动态更新请求头,匹配的参数必须提供给 @Header ,若参数值为 null ,这个头会被省略,否则,会使用参数值的 toString 方法的返回值。
@GET("project")
Call<ProjectBean> getProject3(@Header("Authorization") String authorization);
Body
多用于post请求发送非表单数据,比如想要以post方式传递json格式数据使用 @Body 注解,指定一个对象作为 request body 。
@POST("project/new")
Call<ProjectBean> createProject(@Body ProjectBean user);
Field
- 作用于方法的参数
- 用于发送一个表单请求
- 用String.valueOf()把参数值转换为String,然后进行URL编码,当参数值为null值时,会自动忽略,如果传入的是一个List或array,则为每一个非空的item拼接一个键值对,每一个键值对中的键是相同的,值就是非空item的值,如:name=张三&name=李四&name=王五,另外,如果item的值有空格,在拼接时会自动忽略,例如某个item的值为:张三,则拼接后为name=张三.
//固定或可变数组
@FormUrlEncoded @POST("/list")
Call<ResponseBody> example(@Field("name") String... names);
FieldMap
- 作用于方法的参数
- 用于发送一个表单请求
- map中每一项的键和值都不能为空,否则抛出IllegalArgumentException异常
@FormUrlEncoded
@POST("/examples")
Call<ResponseBody> example(@FieldMap Map<String, String> fields);
Part
- 作用于方法的参数,用于定义Multipart请求的每个part
- 使用该注解定义的参数,参数值可以为空,为空时,则忽略
- 使用该注解定义的参数类型有以下3种方式可选:
- 如果类型是okhttp3.MultipartBody.Part,内容将被直接使用。省略part中的名称,即 @Part MultipartBody.Part part
- 如果类型是RequestBody,那么该值将直接与其内容类型一起使用。在注释中提供part名称(例如,@Part(“foo”)RequestBody foo)
- 其他对象类型将通过使用转换器转换为适当的格式。 在注释中提供part名称(例如,@Part(“foo”)Image photo)
PartMap
- 作用于方法的参数,以map的方式定义Multipart请求的每个part
- map中每一项的键和值都不能为空,否则抛出IllegalArgumentException异常
- 使用该注解定义的参数类型有以下2种方式可选:
- 如果类型是RequestBody,那么该值将直接与其内容类型一起使用
- 其他对象类型将通过使用转换器转换为适当的格式
@Multipart @POST("upload/upload")
Call<ProjectBean> upload5(@FieldMap() Map<String, String> params,
@PartMap() Map<String, RequestBody> files,
@Part("file") RequestBody file,
@PartMap Map<String,RequestBody> maps);
HeaderMap
- 作用于方法的参数,用于添加请求头
- 以map的方式添加多个请求头,map中的key为请求头的名称,value为请求头的值,且value使用
- String.valueOf()统一转换为String类型,
- map中每一项的键和值都不能为空,否则抛出IllegalArgumentException异常
@GET("/example1")
Call<ProjectBean> example1(@HeaderMap Map<String, String> headers);
/使用///
Map<String,String> headers = new HashMap<>();
headers.put("Accept","text/plain");
headers.put("Accept-Charset", "utf-8");
wanAndroidApi.example1(headers)
.enqueue(new Callback<ProjectBean>() {
@Override public void onResponse(Call<ProjectBean> call, Response<ProjectBean> response) {
} ,
@Override public void onFailure(Call<ProjectBean> call, Throwable t) {
}
});
Path
请求 URL 可以替换模块来动态改变,替换模块是 {}包含的字母数字字符串,替换的参数必须使用@Path 注解的相同字符串
@GET("example5/{id}")
Call<ResponseBody> example5(@Path("id") int id);
Query
- 作用于方法的参数
- 用于添加查询参数,即请求参数
- 参数值通过String.valueOf()转换为String并进行URL编码
- 使用该注解定义的参数,参数值可以为空,为空时,忽略该值,当传入一个List或array时,为每个非空item拼接请求键值对,所有的键是统一的,如:name=张三&name=李四&name=王五.
@GET("example2/{id}")
Call<ResponseBody> example2(@Query("id") int id);
QueryMap
复杂的查询参数
@GET("example3/{id}")
Call<ResponseBody> example3(@Path("id") int id, @QueryMap Map<String, String> options);
Url
作用于方法参数
用于添加请求的接口地址
@GET
Call<ResponseBody> example4(@Url String url);
标记类
分类 | 名称 | 备注 |
---|---|---|
表单请求 | FormUrlEncoded | 表示请求实体是一个Form表单,每个键值对需要使用@Field注解 |
请求参数 | Multipart | 表示请求实体是一个支持文件上传的Form表单,需要配合使用@Part,适用于有文件上传的场景 |
标记 | Streaming | 表示响应体的数据用流的方式返回,适用于返回的数据比较大的情况,例如大文件下载。 |
FormUrlEncoded
用于修饰Field注解和FieldMap注解
使用该注解,表示请求正文将使用表单网址编程。字段应该声明为参数,并用@Field注解或@FieldMap注解。使用FormUrlEncoded注解的请求Content-Type请求头为application/x-www-form-urlencoded。字段名称和值将先进行UTF-8进行编码,再根据RFC-3986进行URI编码。
Multipart
上传文件使用: Content-Type:multipart/form-data
Multipart:表示请求实体是一个支持文件上传的Form表单,需要配合使用@Part,适用于有文件 上传的场景
Part:用于表单字段,Part和PartMap与Multipart注解结合使用,适合文件上传的情况
Part 后面支持三种类型,{@link RequestBody}、{@link okhttp3.MultipartBody.Part} 、任意类型;
@param file 服务器指定的上传图片的key值
上传单张图片
@Multipart
@POST("project/upload")
Call<ProjectBean> upload1(@Part("file" + "\";filename=\"" + "test.png") RequestBody file);
@Multipart
@POST("project/xxx")
Call<ProjectBean> upload2(@Part MultipartBody.Part file);
//上传单个图片1
File file = new File("");
RequestBody requestBody = RequestBody.create(MediaType.parse("image/png"),file); wanAndroidApi.upload1(requestBody).execute();
//上传单个图片2
MultipartBody.Part imagePart = MultipartBody.Part.createFormData("上传的 key" ,file.getName(),requestBody); wanAndroidApi.upload2(imagePart).enqueue(new Callback<ProjectBean>() {
@Override public void onResponse(Call<ProjectBean> call, Response<ProjectBean> response) {
} ,
@Override public void onFailure(Call<ProjectBean> call, Throwable t) {
}
});
上传多张图片
@Multipart @POST("project/upload")
Call<ProjectBean> upload3(@PartMap Map<String, RequestBody> map);
@Multipart @POST("project/xxx")
Call<ProjectBean> upload4(@PartMap Map<String, MultipartBody.Part> map);
//上传多张图片1
//图片集合
List<File> files = new ArrayList<>();
Map<String, RequestBody> map = new HashMap<>();
for (int i = 0; i < files.size(); i++) {
RequestBody requestBody = RequestBody.create(MediaType.parse("image/png"), files.get(i));
map.put("file" + i + "\";filename=\"" + files.get(i).getName(), requestBody);
}
wanAndroidApi.upload3(map).execute();
//上传多张图片2
Map<String, MultipartBody.Part> map1 = new HashMap<>();
File file1 = new File("");
RequestBody requestBody1 = RequestBody.create(MediaType.parse("image/png"), file1);
MultipartBody.Part part1 = MultipartBody.Part.createFormData("上传的key1", file1.getName(), requestBody1); map1.put("上传的key1", part1);
File file2 = new File("");
RequestBody requestBody2 = RequestBody.create(MediaType.parse("image/png"), file2);
MultipartBody.Part part2 = MultipartBody.Part.createFormData("上传的key2", file2.getName(), requestBody2); map1.put("上传的key2", part2);
wanAndroidApi.upload4(map1).execute();
图文混传
@Multipart
@POST("upload/upload")
Call<ProjectBean> upload5(@FieldMap() Map<String, String> params, @PartMap() Map<String, RequestBody> files);
/**
* Part 后面支持三种类型,{@link RequestBody}、{@link okhttp3.MultipartBody.Part} 、任意类型;
* @param userName * @param passWord * @param file
* @return
*/
@Multipart @POST("project/xxx")
Call<ProjectBean> upload6(@Part("username") RequestBody userName, @Part("password") RequestBody passWord, @Part MultipartBody.Part file);
MediaType textType = MediaType.parse("text/plain");
RequestBody name = RequestBody.create(textType, "zero");
RequestBody password = RequestBody.create(textType, "123456");
File file = new File("");
RequestBody requestBody = RequestBody.create(MediaType.parse("image/png"), file);
MultipartBody.Part part = MultipartBody.Part.createFormData("上传的 key", file.getName(), requestBody); wanAndroidApi.upload6(name, password, part).enqueue(new Callback<ProjectBean>() {
@Override public void onResponse(Call<ProjectBean> call, Response<ProjectBean> response) {
},
@Override public void onFailure(Call<ProjectBean> call, Throwable t) {
}
});
Streaming
未使用该注解,默认会把数据全部载入内存,之后通过流获取数据也是读取内存中数据,所以返回数据
较大时,需要使用该注解
/**
* 12.Streaming注解:表示响应体的数据用流的方式返回,适用于返回的数据比较大,该注解在下载大文件的特别有用
*/
@Streaming
@GET
Call<ProjectBean> downloadFile(@Url String fileUrl);
关键类功能说明
Retrofit
Retrofifit提供的子系统
-
serviceMethodCache(自定义的接口映射对象集合)
-
baseUrl(请求地址)
-
callFactory(默认为OKHttpCall)
-
converterFactories(数据解析器工厂集合)
-
callAdapterFactories(Call适配器工厂集合)
-
callbackExecutor(回调执行,Android平台默认为MainThreadExecutor)
使用Builder模型构建(把对象依赖的零件创建、零件的组装封装起来;以使客户很方便的获取一个复杂对象;)
Platform
Retrofit中用来管理多平台的方法,支持Android、Java8。通过findPlatform获取对应的平台,同时也初始化了defaultCallAdapterFactory工厂
ServiceMethod
接口映射的网络请求对象,通过动态代理,将自定义接口的标注转换为该对象,将标注及参数生成OkHttp所需的Request对象。Retrofit的create通过动态代理拦截,将每一个自定义接口转换成为一个ServiceMethod对象,并通过通过serviceMethodCache进行缓存
Call
Retrofifit定义的网络请求接口,包含execute、enqueue等方法
OkHttpCall
Ohttp的Call实现,通过createRawCall得到真正的 okhttp3.Call对象,用于进行实际的网络请求
CallAdapter.Factory
CallAdapter的静态工厂,包含get的抽象方法,用于生产CallAdapter对象
ExecutorCallAdapterFactory
Android平台默认的CallAdapter工厂,get方法使用匿名内部类实现CallAdapter,返回ExecutorCallbackCall,实现了Call
ExecutorCallbackCall
采用静态代理设计,delegate实际为OkHttpCall,使用callbackExecutor实现回调在主线程中执行
RxJavaCallAdapterFactory
Rxjava平台的CallAdapter工厂,get方法返回RxJavaCallAdapter对象
RxJavaCallAdapter
Rxjava平台的设配器,返回observable对象
Converter.Factory
数据解析器工厂,用于生产Converter实例
GsonConverterFactory
数据解析工厂实例,返回了GsonResponseBodyConverter数据解析器
GsonResponseBodyConverter
Gson的数据解析器,将服务端返回的json对象转换成对应的java模型
Response
Retrofifit网络请求响应的Response
Retrofifit中的设计模式
-
建造者模式
Retrofifit对象的创建、ServiceMethod对象创建都使用Build模式,将复杂对象的创建和表示分离,调用者不需要知道复杂的创建过程,使用Build的相关方法进行配置创建对象。
-
外观模式
Retrofifit对外提供了统一的调度,屏蔽了内部的实现,使得使用该网络库简单便捷。门面模式: 提供一个统一的接口去访问多个子系统的多个不同的接口,它为子系统中的一组接口提供一个统一的高层接口。使用子系统更容易使用
-
动态代理模式
通过动态代理的方式,当调用Retrofifit的create()方法时,会进行动态代理监听。当执行具体的接口方法时,会回调InvocationHandler。通过反射解析method的标注及参数,生成ServiceMethod对象。
-
静态代理模式
Android平台默认的适配器ExecutorCallbackCall,采用静态代理的模式。具体的实现delegate为OkHttpCall。
-
工厂模式
Converter及CallAdapter的创建都采用了工厂模式进行创建。
-
适配器模式
CallAdapter的adapt采用了适配器模式,使得interface的返回对象可以动态扩展,增强了灵活性