Retrofit:网络框架
Created|Updated
|Word Count:2.5k|Reading Time:9mins|Post Views:
Retrofit:网络框架
前言
从我刚开始Android开发的时候,我总会羡慕大牛们写的APP都是网络请求数据相关的,总觉得这个网络请求是最难的部分,当我学完JAVA和Kotlin的时候,再回头看网络,其实都是由输入输出流来放入Socket套接字进行UDP连接。但是对于我刚开始学的时候,总是需要不停地看Retrofit的介绍和博客,总是很复杂,感觉没有最明确的告诉你如何去使用,于是一年多使用了几次Retrofit的时候,想要自己去写一篇博客,以供自己回顾。
(注:前面可以只看什么是网络请求和如何利用Java使用Retrofit,源码解析及以后的可以以后在写)
什么是网络请求?
当我们看一些牛人的代码的时候,我们总能看到一些仓库数据类叫什么RemoteRepo,LocalRepo 什么意思?就是说我的APP数据来源于两个地方:有可能是我请求服务器时候拿来的数据(个人信息等等),有可能是我本地存储的数据(历史记录等等),在互联网中请求不属于本地服务器的数据,就是网络请求。在网络请求中,我们分为了几个步骤:
- 配置网络请求参数(Build request)
- 创建网络请求对象 (Call requesut)
- 发送网络请求 (Call request)
解析数据 (Converter(转换器))
- 返回数据给对象
- 拿到对象UI展示
我们把网络请求分为这几步,具体其实分的很细,接下来我们将具体怎么做的时候,会来分别的说清楚这几步。
什么是Retrofit?(Retrofit2.0)
每当我看很多博客的时候,对于Retrofit的介绍这里,总是很多很多看不懂的东西。在这里,如果对于一个框架进行一个介绍,那么那就是一个API,一套封装好的库来进行开发,但是对于Retrofit来说,这个封装是针对另一个框架Okhttp的封装,所以对于网络请求来说,就是Okhttp在做,我们只是用封装的封装罢了。
那么Retrofit怎么去做网络处理请求呢?
Build request
通过注解的形式来进行网络请求配置请求参数
Call request
通过动态代理创建网络请求的执行器和适配器并且发送网络请求
Converter
服务器返回数据以后去利用转换器来转换接收的数据
Executor
切换线程,处理数据
这里我想说一下为什么要切换线程?对于Android来说,我们总是期待用户进行点击或者一些活动的时候APP给予最快速率的响应,但是在我们系统来说有很多耗时的操作,我们为了加快响应用户的行为而对数据的加载切换到子线程中进行,而在主线程(UI、Activity、Fragment)来进行呈现数据给用户。(具体见引用中对于多线程任务的描述)
总结:Retrofit经过几年的发展,以独特方便的注解方式、高效性能、设计风格广受开发者的好评,成为近几年来最受欢迎的网络请求框架。
如何使用Retrofit?(Java)
项目地址:
对于一个项目而言,首先要明确你是有一个数据类,有一个封装着网络配置参数的类,在MainActivity中进行切换线程的网络请求操作和(无优化,后面会写如何优化)
首先要明确,后端给你的api文档大概是这样的

其他的也是大同小异,如果给你.yml文件在这个网站打开也是这个样
看不懂?不要紧,我们一步一步来
如果我们想要请求一个User.login的数据,如何创建数据类呢?
首先我们应该查看api文档中的Model中的你需要请求数据的Json格式数据

也就是这里的
1 2 3 4 5 6 7 8 9
| id integer($int64) username string firstName string lastName string email string password string phone string userStatus integer($int32) User Status
|
在Android Studio中利用一个插件来自动生产我们的数据类GsonFormaster(使用方法推荐视频:https://www.bilibili.com/video/BV1WJ411X7Jt?p=5(10分钟就能完成))
最后你就可以得到一个类似与这样的类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
| package com.example.film_review.firstpage.attention;
public class AttentionItemData {
private String User_id; private String Name; private String User_picture; private int Review_id; private String Title; private String Content; private String Time; private String Tag; private String Picture; private int Comment_sum; private int Like_sum;
public String getUser_id() { return User_id; }
public void setUser_id(String User_id) { this.User_id = User_id; }
public String getName() { return Name; }
public void setName(String Name) { this.Name = Name; }
public String getUser_picture() { return User_picture; }
public void setUser_picture(String User_picture) { this.User_picture = User_picture; }
public int getReview_id() { return Review_id; }
public void setReview_id(int Review_id) { this.Review_id = Review_id; }
public String getTitle() { return Title; }
public void setTitle(String Title) { this.Title = Title; }
public String getContent() { return Content; }
public void setContent(String Content) { this.Content = Content; }
public String getTime() { return Time; }
public void setTime(String Time) { this.Time = Time; }
public String getTag() { return Tag; }
public void setTag(String Tag) { this.Tag = Tag; }
public String getPicture() { return Picture; }
public void setPicture(String Picture) { this.Picture = Picture; }
public int getComment_sum() { return Comment_sum; }
public void setComment_sum(int Comment_sum) { this.Comment_sum = Comment_sum; }
public int getLike_sum() { return Like_sum; }
public void setLike_sum(int Like_sum) { this.Like_sum = Like_sum; } }
|
这个类里面有什么?有一些参数,最后接收的网络请求数据封装在这个类里面,自动的有一些get与set方法
好了在正式开始Retrofit2.0之前,我们首先要了解一下什么是异步通信
Retrofit2.0采用的是异步通信的方法来进行网络请求。那有人要问了,这个异步和我有什么关系??我凭什么要了解这个事情,因为了解异步有助于去写一个更好的网络请求框架。
异步通信用数字逻辑的话来说就是没有使用同一个时钟源,也就是说网络请求分为什么?发送与接收。一般怎么做?客户端先发送网络请求,等发完了以后进行接收,这就是同步,也就是有先后顺序。而异步是我接收与发送是分开的,我这边接收是一个整体,发送是另一个整体,在发送之前我就已经准备好了接收,随时准备接收,而发送是可以晚一点进行的。
所以我们现在搞清楚了以后,来正式的写Retfoit
- 实例化Retrofit对象,创建网络请求接口实例、配置参数(Bulid request)
1 2
| Retrofit retrofit = new Retrofit.Builder() .baseUrl("www.baidu.com")
|
- 异步的准备好接收数据(Converter)
1 2 3 4
| Retrofit retrofit = new Retrofit.Builder() .baseUrl("www.baidu.com") .addConverterFactory(GsonConverterFactory.create()) .build();
|
猜猜下一步要干什么?是发送数据!我们来这样想,Retrofit首先先配置好,然后创建好接收数据的客户端,最后来进行发送数据。
- 创建Retrofit接口!!!!前面的Retrofit是一个Class,而后面的是一个接口!
1 2 3 4 5 6
| public interface RetrofitAPI { @GET("api/v1/grounds/{user_id}") Call<List<AttentionItemData>> getAttention(@Header("token") String token, @Path("user_id") String user_id); }
|
- 在处理网络请求util类中实现接口端点(没有进行实例化对象,就是一个引用)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| RetrofitAPI API = retrofit.create(RetrofitAPI.class); API.enqueue(new Callback<List<AttentionItemData>>() { @Override public void onResponse(Call<List<AttentionItemData>> call, Response<List<AttentionItemData>> response) {
} @Override public void onFailure(Call<List<AttentionItemData>> call, Throwable t) { Log.e("TAG", "FAILURE"); t.printStackTrace(); } });
|
源码解析
在介绍了这些以后,我们还是需要来分析一下源码,在retrofit2的这种封装下,其实源码使用的是动态代理模式,我们看似只是通过接口从而拿到了方法,其实内部做了一个代理。具体如下:
我们点击enqueue方法来分析一下
1 2 3 4 5
| public interface Call<T> extends Cloneable {
void enqueue(Callback<T> callback);
}
|
主要就是通过接口能够看到我们具体返回的对象是谁返回的list对象是谁
重点看一下retrofit.create(类.class)方法的逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public <T> T create(final Class<T> service) { validateServiceInterface(service); return (T) Proxy.newProxyInstance( service.getClassLoader(), new Class<?>[] {service}, new InvocationHandler() { private final Platform platform = Platform.get(); private final Object[] emptyArgs = new Object[0];
@Override public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable { if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } args = args != null ? args : emptyArgs; return platform.isDefaultMethod(method) ? platform.invokeDefaultMethod(method, service, proxy, args) : loadServiceMethod(method).invoke(args); } }); }
|
重点是注解1,是一个动态代理,返回的是我们定义的RetrofitAPI的实例,说到动态代理,我们来讨论一下
动态代理就是实现了一个接口的对象,拦截接口的方法并且成功的添加的一些东西,是不是有点像Kotlin的扩展函数,只不过扩展函数是给类加了一些方法。
而对于Retrofit2来说:拦截到方法、参数,再根据我们在方法上的注解,去拼接为一个正常的Okhttp请求,然后执行。