要不要写这篇文章,其实我纠结了好久,因为网上已经有好多的关于Retrofit的文章了,不乏有很经典的文章和博客,读后真的收益良多,相比以前的只会用,成长良多!但是,看完以后始终觉着那些东西是非而非,时有感觉那都不是自己的东西,毕竟那不是按照自己的思路来的,所以有必要按照自己的思路再来捋一遍,人啊,关键得有自己的想法啊,不管是在编程上还是人生路上!
贴一个我感觉写的不错的关于Retrofit的博客,就不转载了
我也把我的心得及思路记录一下,希望朋友们也多思考,毕竟思考也是成功他老母啊!
Retrofit2是基于okhttp的类型安全HTTP客户端,retrofit2只需要专注对请求的封装处理,而真正的网络请求是由okhttp发起的,然后处理返回的响应。
支持转换器序列化数据,也可以自定以转化器
基于Retrofit2是对请求接口的封装,,而我们只去关注怎样去构造一个可用的URL就行了,至于他底层怎样去处理(注解+动态代理)我们就不用去管了。
按照我们分析Okhttp3的套路来一遍(这只是我自己的思路,大家尽量按自己的思路来理解),先整体把握一下Retrofit2的内容,了解其整体构成,然后到局部使用,再到底层原理
整体大概—–具体细节——-底层原理
Retrofit2整个项目分成两个包
接口 | 功能 |
---|---|
Call | 向web服务器发送请求并返回响应。 |
CallAdapter | 响应适配特定对象的链接调用 |
Callback | 请求回调 |
Converter | 将HTTP转换为对象。使用工厂设计模式 |
类 | 功能 |
---|---|
BuiltInConverters | Retrofit2内置的简单数据转换器(5种) |
DefaultCallAdapterFactory | 为I/O和应用程序级别的相同线程创建调用适配器。 |
ExecutorCallAdapterFactory | 新的线程创建调用适配器 |
HttpException | 异常,非2xx HTTP响应。 |
OkHttpCall | 对Okhhtp3的包装 |
ParameterHandler | 参数处理程序,将我们解析的注解参数设置到RequestBuilder里 |
Platform | 返回当前平台 |
RequestBuilder | 配置请求参数 |
Response | HTTP响应 |
Retrofit | 通过在声明的方法上使用注解,可以将Java接口转换为HTTP调用。 |
ServiceMethod | 将接口方法的调用转换为HTTP调用。其实Retrofit的creat方法就是返回的其的方法调用。 |
Utils | 工具类 |
注解 | 功能(这里,我分为4种注解:方法注解;标记注解;参数注解;其他) |
---|---|
方法注解 | 表示我们是以什么方法提交HTTP请求 |
DELETE | 请求删除URL指向的资源。 |
GET | get请求 |
HEAD | 请求Request-URI所标识的资源响应消息报头,HEAD方法可以在响应时不返回消息体。 |
HTTP | 使用自定义HTTP请求方法 |
OPTIONS | 请求查询服务器的性能,或者查询与资源相关的选项。 |
PATCH | PATCH方法是新引入的,是对PUT方法的补充,用来对已知资源进行局部更新 |
POST | post请求 |
PUT | 与GET相反,请求服务器存储一个资源,并用Request-URI做为其标识 |
标记注解 | 标识此次请求的特性 |
FormUrlEncoded | 表示请求体将使用表单URL编码 |
Multipart | 表示支持文件上传,需要上传文件时需要在方法前添加次注解 |
Streaming | 表示处理返回的响应流,通常用于大文件下载 |
参数注解 | 为构建正常请求配置的参数 |
Body | 适用于POST/PUT请求,参数有多个,可以封装成Body对象 |
Field | Post方式传递简单的键值对,需要添加@FormUrlEncoded表示表单提交 |
FieldMap | 为表单编码的请求指定键/值对。将Field封装成map集合 |
Part | 文件上传 |
PartMap | 以map的形式上传文件 |
Query | 查询参数附加到URL |
QueryMap | Query参数比较多,那么可以通过@QueryMap方式将所有的参数集成在一个Map统一传递 |
QueryName | 适用于参数没有值的情况 |
其他 | |
Header | 替换请求头 |
HeaderMap | 需要替换的请求头比较多,可以添加到Map集合里进行统一提交 |
Headers | 将值添加到请求头中 |
Path | URL占位符,用于替换和动态更新URL中的字符 |
Url | 对baseUrl不统一的情况需要此注解,用全路径覆盖baseUrl。 |
到现在,我们对Retrofit2整体架构已经有了大概的了解,至于使用那就更加简单了。
使用流程
//建议 以你http返回的类型命名类型,若是Restful API风格那就更好了,可以把一系列方法都定义在这一个接口里
public interface NewService {
//baseURL(Retrofit里配置http://image.baidu.com/data/)+方法注解(GET)里的字符(imgs)+方法里面(Query注解的参数)构成一个完整资源路径
/**
* 获取美女图片
* API获取途径http://www.jb51.net/article/61266.htm
* eg: http://image.baidu.com/data/imgs?sort=0&pn=0&rn=20&col=美女&tag=全部&tag3=&p=channel&from=1
* 通过分析,推断并验证了其中字段的含义,col表示频道,tag表示的是全部的美女,也可以是其他Tag,pn表示从第几张图片开始,rn表示获取多少张
* @param
* @return
*/
//这个是适配Rxjava2以后的方法
@Headers(CACHE_CONTROL_NETWORK)
@GET("imgs")
Observable getWelfarePhoto(@Query("sort") int sort,
@Query("pn")int startImage,
@Query("rn")int size,
@Query("col")String col,
@Query("tag")String tag,
@Query("tag3")String tag3,
@Query("p")String channel,
@Query("from")int from);
//这个是原始的
@Headers(CACHE_CONTROL_NETWORK)
@GET("imgs")
Call getWelfarePhotoCall(@Query("sort") int sort,
@Query("pn")int startImage,
@Query("rn")int size,
@Query("col")String col,
@Query("tag")String tag,
@Query("tag3")String tag3,
@Query("p")String channel,
@Query("from")int from);
}
public static T getInstanceStringUrl(String url,Class clazz){
Retrofit retrofit = new Retrofit.Builder()
// .client()//配置OKhttp客户端
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())//json转对象
// .validateEagerly() 在Call时,是否立即验证接口中的方法配置
// .callbackExecutor() //回调
.build();
T service = retrofit.create(clazz);
return service;
}
RetrofitClient.getInstanceStringUrl("http://image.baidu.com/data/", NewService.class)
.getWelfarePhotoCall(0,0,50,"美女","全部", "", "channel", 1)
.enqueue(new retrofit2.Callback() {
@Override
public void onResponse(retrofit2.Call call, final retrofit2.Response response) { //因为底层是基于okhttp进行网络请求,所以需要我们自己进行线程间转换,若适配Rxjava后就没这么麻烦了。
//请求成功回调方法
runOnUiThread(new Runnable() {
@Override
public void run() {
tvShow.setText(response.body().col+":"+response.body().col);
}
});
}
@Override
public void onFailure(retrofit2.Call call, final Throwable t) {
//请求失败回调方法
runOnUiThread(new Runnable() {
@Override
public void run() {
tvShow.setText(t.getMessage());
}
});
}
});
配置okhttp:一般我们网络请求配置一个全局OKHTTP就行了,我一般是在Application里初始化。
public static void initNet(Context context ,Cache cache, Interceptor interceptor, Interceptor netInterceptor){
if(cache==null){
//缓存目录
cache = new Cache(new File(context.getCacheDir(),"cache"),1024*1024*1024);
}
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.cache(cache)
.connectTimeout(10, TimeUnit.SECONDS)
.retryOnConnectionFailure(true);
if(interceptor!=null){
builder.addInterceptor(interceptor);
}
if(netInterceptor!=null){
builder.addNetworkInterceptor(netInterceptor);
}
okHttpClient = builder.build();
}
下面是rxjava版的请求
NetUtils.getService(MainActivity.this)
.subscribe(new Observer() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull BeautyPicture beautyPicture) {
tvShow.setText(beautyPicture.col+":"+beautyPicture.getTag());
}
@Override
public void onError(@NonNull Throwable e) {
}
@Override
public void onComplete() {
}
});
NetUtils类,我在这里做了一层封装,若有多个HTTP API ,直接在这里调用就行,就不用新建Retrofit配置了,因为大部分APP里的网络配置都是一样的
public class NetUtils {
private static ObservableTransformer transformer = getSchedulers();
public static Observable getService(RxActivity activity){
NewService newService = RetrofitClient.getOkhttpClientServiceStringUrl("http://image.baidu.com/data/", NewService.class);
return newService.getWelfarePhoto(0,0,50,"美女","全部", "", "channel", 1)
.compose(transformer)
.compose(activity.bindToLifecycle());
}
private static ObservableTransformer getSchedulers(){
return new ObservableTransformer() {
@Override
public ObservableSource apply(@NonNull Observable upstream) {
return upstream.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
};
}
}
public static T getOkhttpClientServiceStringUrl(String url,Class tClass){
Retrofit retrofit = new Retrofit.Builder()
.client(okHttpClient)
.baseUrl(url)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
T service = retrofit.create(tClass);
return service;
}
这样我们的网络请求到数据显示都完成了,当然上面只展示GET注解,下面我们来看一下其他的一些常用注解的使用方式。
// http://app.pearvideo.com/clt/jsp/v2/home.jsp
// http://app.pearvideo.com/clt/jsp/v2/getCategoryConts.jsp
// http://app.pearvideo.com/clt/jsp/v2/content.jsp?contId=1064146
//这里我实际抓取了一下Get也可以
@FormUrlEncoded
@POST("getCategoryConts.jsp")
Observable getKankanVideoFromCate(@HeaderMap HashMap headers,
@Field("hotPageidx")int page,
@Field("start")int start,
@Field("categoryId")String categoryId);
下面是我截取我的一个开源项目里的一个使用(Kotlin写的)
init {
headersMap.put("X-Channel-Code","official")
headersMap.put("X-Client-Agent","Xiaomi")
headersMap.put("X-Client-Hash","2f3d6ffkda95dlz2fhju8d3s6dfges3t")
headersMap.put("X-Client-ID","123456789123456")
headersMap.put("X-Client-Version","2.3.2")
headersMap.put("X-Long-Token","")
headersMap.put("X-Platform-Type","0")
headersMap.put("X-Platform-Version","5.0")
headersMap.put("X-User-ID","")
}
fun getCategoryDate(categoryId: String) {
mCategoryId = categoryId
var time = System.currentTimeMillis()/1000
headersMap.put("X-Serial-Num",time.toString())
start = 0
RetrofitService.getKankanVideoFromCate(headersMap,page,start,categoryId)
.doOnSubscribe { mView.showLoading() }
.compose(mView.bindToLife())
.subscribe(object :Observer{
override fun onComplete() {
mView.hideLoading()
}
override fun onSubscribe(d: Disposable) {
}
override fun onNext(t: VideoListDate) {
mView.loadVideoForCategoryidDate(t)
page++
start+=t.contList?.size!!
}
override fun onError(e: Throwable) {
mView.showNetError(object :EmptyErrLayout.OnReTryListener{
override fun onReTry() {
getCategoryDate(categoryId)
}
})
}
})
}
其他几种情况我就没遇到过了,请允许我在此贴一些网上的事例代码,但是通用的模式都是,我们根据实际注释,配置我们需要的模块就可以了
/**
* 单张图片上传
* retrofit 2.0的上传和以前略有不同,需要借助@Multipart注解、@Part和MultipartBody实现。
*
* @param url
* @param file
* @return
*/
@Multipart
@POST("{url}")
Call> upload(@Path("url") String url, @Part MultipartBody.Part file);
/**
* 多张图片上传
*
* @param map
* @return
*/
@Multipart
@POST("upload/upload")
Call> upload(@PartMap Map map);
/**
* 图文混传
*
* @param post
* @param map
* @return
*/
@Multipart
@POST("")
Call> register(@Body News post, @PartMap Map map);
/**
* 这里需要注意的是如果下载的文件较大,比如在10m以上,那么强烈建议你使用@Streaming进行注解,否则将会出现IO异常.
*
* @param fileUrl
* @return
*/
@Streaming
@GET
Observable> downloadPicture2(@Url String fileUrl);
以上接口实例出自 作者:wo叫天然呆
链接:https://www.jianshu.com/p/73216939806a
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
Okhttp3+Retrofit2+RxJava2的基本使用,我们基本都了解完了,很简单的对吧!
上面用到的一些代码戳这里