第三方-Retrofit

Retrofit

Retrofit2:一个网络请求的适配器,将一个基本的Java接口通过动态代理的方式翻译成一个HTTP请求,并通过OkHttp去发送请求。此外它还具有强大的可扩展性,支持各种格式转换以及RxJava。

参考链接

简单例子:

  1. 创建个请求的api接口,通过注解方式进行接口请求
1
2
3
4
5
6
7
8
9
10
public interface PersonalProtocol{
/**
* 用户信息
* @param page
* @return
*/
@FormUrlEncoded
@POST("user/personal_list_info")
Call<Response<PersonalInfo>> getPersonalListInfo(@Field("cur_page") int page);
}

@FormUrlEncoded注解表示Form表单,另外还有@Multipart等注解,如果接口不需要传递参数,那么@FormUrlEncoded以及@Multipart需要去掉,具体原因后面章节会讲到。
@POST表示post请求,此外还可以使用@GET请求

  1. 使用Retrofit来请求数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private void requestRetrofit(){
//其中build()内有创建了okhttpClient(底层用的okhttp3)
Retrofit retrofit = new Retrofit.Builder().baseUrl("www.xxx.com").build();
PersonalProtocol personalProtocol = retrofit.create(PersonalProtocol.class);
//Retrofit2中Call接口的默认实现是OkHttpCall,它默认使用OkHttp3作为底层http请求client
Call<Response<PersonalInfo>> call = personalProtocol.getPersonalListInfo(12);
call.enqueue(new Callback<Response<PersonalInfo>>(){
@Override
public void onResponse(Call<Response<PersonalInfo>> call, Response<Response<PersonalInfo>> response) {
//数据请求成功
}

@Override
public void onFailure(Call<Response<PersonalInfo>> call, Throwable t) {
//数据请求失败
}
});
}

上面例子的Retrofit对象还可以指定OkHttpClient等设置

1
2
3
4
5
OkHttpClient okHttpClient = new OkHttpClient();
Retrofit retrofit = new Retrofit.Builder().baseUrl("www.xxx.com")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create(buildGson()))
.build();

与RxJava配合使用

1
2
3
4
5
6
7
8
9
10
11
public interface PersonalProtocol {
/**
* 用户信息
* @param page
* @return
*/
@FormUrlEncoded
@POST("user/personal_list_info")
Observable<PersonalInfo> getPersonalListInfo(@Field("cur_page") int page);
// Call<Response<PersonalInfo>> getPersonalListInfo(@Field("cur_page") int page);
}
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
private void requestRetrofit(){
OkHttpClient okHttpClient = new OkHttpClient();
Retrofit retrofit = new Retrofit.Builder().baseUrl("www.xxx.com")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create(buildGson()))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
PersonalProtocol personalProtocol = retrofit.create(PersonalProtocol.class);
rx.Observable<PersonalInfo> observable = personalProtocol.getPersonalListInfo(12);
observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())//最后在主线程中执行
.subscribe(new Subscriber<PersonalInfo>() {
@Override
public void onCompleted() {

}

@Override
public void onError(Throwable e) {
//请求失败
}

@Override
public void onNext(PersonalInfo personalInfo) {
//请求成功
}
});
}

完整例子

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
//app\build.gradle
apply plugin: 'com.android.application'

android {
//...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
//...
}

dependencies {
//...

//network
api("com.squareup.retrofit2:retrofit:2.6.0}") {
exclude module: 'okhttp'
exclude module: 'okio'
}
implementation("com.squareup.retrofit2:converter-gson:2.6.0"]) {
exclude module: 'gson'
exclude module: 'okhttp'
exclude module: 'okio'
exclude module: 'retrofit'
}
implementation("com.squareup.retrofit2:adapter-rxjava2:2.6.0"]) {
exclude module: 'rxjava'
exclude module: 'okhttp'
exclude module: 'retrofit'
exclude module: 'okio'
}
implementation "com.squareup.okhttp3:okhttp:3.12.3"
implementation "com.google.code.gson:gson:2.8.5"

//rxjava
implementation "io.reactivex.rxjava2:rxjava:2.2.2"
implementation "io.reactivex.rxjava2:rxandroid:2.1.0"

//loggerInterceptor
implementation "com.squareup.okhttp3:logging-interceptor:3.11.0"

//Klog
implementation "com.github.zhaokaiqiang.klog:library:1.6.0"
}
1
2
3
4
5
6
7
8
//AndroidManifest.xml
<manifest ...>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
//...
</manifest>
  • 创建公共常量类
1
2
3
4
public class Constants {
// 测试暂时用 玩Android api
public static final String BASE_URL = "https://www.wanandroid.com";
}
  • 创建首页的几个实体类
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
public class HomeArticleEntity implements Serializable {
private int errorCode;
private String errorMsg;
private ArticleData data;

public int getErrorCode() {
return errorCode;
}

public void setErrorCode(int errorCode) {
this.errorCode = errorCode;
}

public String getErrorMsg() {
return errorMsg;
}

public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}

public ArticleData getData() {
return data;
}

public void setData(ArticleData data) {
this.data = data;
}
}
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
public class HomeArticleDetail implements Serializable {
private Long cache_id;
private String author;
private String chapterName;
private boolean collect;
private int id;
private String link;
private String niceDate;
private String superChapterName;
private String title;
private int type;

public Long getCache_id() {
return cache_id;
}

public void setCache_id(Long cache_id) {
this.cache_id = cache_id;
}

public String getAuthor() {
return author;
}

public void setAuthor(String author) {
this.author = author;
}

public String getChapterName() {
return chapterName;
}

public void setChapterName(String chapterName) {
this.chapterName = chapterName;
}

public boolean isCollect() {
return collect;
}

public void setCollect(boolean collect) {
this.collect = collect;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getLink() {
return link;
}

public void setLink(String link) {
this.link = link;
}

public String getNiceDate() {
return niceDate;
}

public void setNiceDate(String niceDate) {
this.niceDate = niceDate;
}

public String getSuperChapterName() {
return superChapterName;
}

public void setSuperChapterName(String superChapterName) {
this.superChapterName = superChapterName;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public int getType() {
return type;
}

public void setType(int type) {
this.type = type;
}
}
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
public class ArticleData implements Serializable {
private int curPage;
private int offset;
private boolean over;
private int pageCount;
private int size;
private int total;
private List<HomeArticleDetail> datas;

public int getCurPage() {
return curPage;
}

public void setCurPage(int curPage) {
this.curPage = curPage;
}

public int getOffset() {
return offset;
}

public void setOffset(int offset) {
this.offset = offset;
}

public boolean isOver() {
return over;
}

public void setOver(boolean over) {
this.over = over;
}

public int getPageCount() {
return pageCount;
}

public void setPageCount(int pageCount) {
this.pageCount = pageCount;
}

public int getSize() {
return size;
}

public void setSize(int size) {
this.size = size;
}

public int getTotal() {
return total;
}

public void setTotal(int total) {
this.total = total;
}

public List<HomeArticleDetail> getDatas() {
return datas;
}

public void setDatas(List<HomeArticleDetail> datas) {
this.datas = datas;
}
}
  • 网络
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/**
* <pre>
* @author : shenbh
* blog : http://shenbh.top
* time : 2020/5/26 17:08
* email :shenbh@qq.com
* desc : 返回数据处理
* </pre>
*/
public abstract class BaseObservaer<T> implements Observer<Response<T>> {
private final String TAG = BaseObservaer.class.getName();

@Override
public void onSubscribe(Disposable d) {
KLog.v(TAG, "onSubscribe:" + d.isDisposed());
}

@Override
public void onNext(Response<T> tResponse) {
if (tResponse.isSuccessful()){
onSuccess(tResponse.body());
} else {
onFailure(tResponse.message());
}
}

@Override
public void onComplete() {
KLog.v(TAG, "...onComplete...");
}

@Override
public void onError(Throwable e) {
KLog.v(TAG, "onError:" + e.getMessage());
}

/**
* 网络请求失败时回调的方法
* @param message
*/
public abstract void onFailure(String message);
/**
* 网络请求成功时回调的方法
* @param data
*/
public abstract void onSuccess(T data);
}


/**
* <pre>
* @author : shenbh
* blog : http://shenbh.top
* time : 2020/5/26 17:03
* email :shenbh@qq.com
* desc : 线程切换等操作
* </pre>
*/
public class NetScheduler {
public static <T> ObservableTransformer<T,T> compose(){
return upstream->upstream.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.debounce(1, TimeUnit.SECONDS);//防止1s内重复请求
}
}

/**
* <pre>
* @author : shenbh
* blog : http://shenbh.top
* time : 2020/5/26 16:36
* email :shenbh@qq.com
* desc : 所有请求的api
* </pre>
*/
public interface Api {
/**
* 首页文章
* @param page page
* @return Observable<HomeArticleEntity>
*/
@GET("/article/list/{page}/json")
Observable<Response<HomeArticleEntity>> homeArticles(@Path("page") int page);
}

/**
* <pre>
* @author : shenbh
* blog : http://shenbh.top
* time : 2020/5/26 16:42
* email :shenbh@qq.com
* desc : api管理类
* </pre>
*/
public class ApiManager {
private Retrofit client;

private static volatile Api INSTANCE;
public static Api getApiInstance(){
if (INSTANCE == null){
synchronized(ApiManager.class){
if (INSTANCE == null){
INSTANCE = new ApiManager().getApi();
}
}
}
return INSTANCE;
}

private Api getApi() {
return client.create(Api.class);
}

private ApiManager(){
client = new Retrofit.Builder()
.baseUrl(Constants.BASE_URL)
.client(getHttpClient())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
}

private OkHttpClient getHttpClient() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
//修改日志拦截器,使用KLog库打印网络日志
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
// 如果是 json 格式内容则打印 json
boolean isJson = (message.startsWith("{") && message.endsWith("}")) ||
(message.startsWith("[") && message.endsWith("]"));
if (isJson) {
KLogUtil.printLine("network request",true);
KLog.v("network request",message);
KLogUtil.printLine("network request",false);
} else{
KLog.v("network request",message);
}
}
});
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
builder.addNetworkInterceptor(httpLoggingInterceptor)
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS);
return builder.build();
}
}
  • Activity中使用
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
public class MainActivity extends AppCompatActivity {
public static final String BASE_URL = "https://www.wanandroid.com";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
wanandroidHomeData();
}

/**
* 玩安卓首页文章数据请求
*/
private void wanandroidHomeData(){
ApiManager.getApiInstance().homeArticles(0)
.compose(NetScheduler.compose())
.subscribe(new BaseObservaer<HomeArticleEntity>(){

@Override
public void onFailure(String message) {
//网络请求失败后的处理

}

@Override
public void onSuccess(HomeArticleEntity data) {
//网络请求成功后的处理

}
});
}
}

Retrofit(hencoder)

Retrofit使用方法简介

  1. 创建一个interface作为Web Service的请求稽核,在里面用注解(Annotation)写入需要配置的请求方法

    1
    2
    3
    4
    public interface GitHubService{
    @GET("users/{user}/repos")
    Call<List<Repo>> listRepos(@Path("user") String user);
    }
  2. 在正式代码里用Retrofit创建出 interface 的实例

    1
    2
    3
    4
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();
    GitHubService service = retrofit.create(GitHubService.class);
  3. 调用创建出的 Service 实例的对应方法,创建出相应的可以用来发起网络请求的Call对象

    1
    Call<List<Repo>> repos = service.listRepos("octocat");
  4. 使用Call.execute()或者Call.enqueue()来发起请求

    1
    repos.enqueue(callback);

Retrofit源码结构总结

  • 通过Retrofit.create(Class)方法创建出 Service interface 的实例,从而使得 Service 中配置的方法变得可用,这是 Retrofit 代码结构得核心;

  • Retrofit.create()方法内部,使用的是Proxy.newProxyInstance()方法来创建 Service 实例。这个方法会为参数中的多个 interface (具体到 Retrofit 来说,是固定传入一个 interface )创建一个对象,这个对象实现了所有 interface 的每个方法,并且每个方法的实现都是雷同的:调用对象实例内部的一个 InvocationHandler成员变量的 invoke() 方法,并把自己的方法信息传递进去。这样就在实质上实现了代理逻辑:interface 中的方法全部由一个另外设定的 InvocationHandler对象来进行代理操作。并且,这些方法的具体实现是在运行时生成 interface 实例时才确定的,而不是在编译时(虽然在编译时就已经可以通过代码逻辑推断出来)。这就是网上所说的 “动态代理机制” 的具体含义。

  • 因此,invoke()方法中的逻辑,就是 Retrofit 创建 Service 实例的关键。这个方法内有三行关键代码,共同组成了具体逻辑:

    1. ServiceMethod的创建:

      1
      ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object,Object>) loadServiceMethod(method);

      这行代码负责读取 interface 中原方法的信息(包括返回值类型、方法注解、参数类型、参数注解),并将这些信息做初步分析。

    2. OkHttpCall的创建:

      1
      OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);

      OkHttpCallretrofit2.Call的子类。这行代码负责将 ServiceMethod 封装进一个retrofit2.Call对象;而这个对象可以在需要的时候(例如它的enqueue()方法被调用的时候,利用 ServiceMethod 中包含的信息来创建一个 okhttp3.Call 对象,并调用这个okhttp3.Call对象来进行网络请求的发起,然后对结构进行预处理(如类型转换)。

    3. adapt()方法:

      1
      return serviceMethod.adapt(okHttpCall);

      这个方法会使用 ServiceMethod 中的 callAdapter 对象来把 okHttpCall 对象进行转换,生成一个新的 retrofit2.Call 对象,在这个新的 Call 对象中,后台线程发起的请求,会在相应返回后,从主线程中调用回调方法,实现线程的自动切换。

      另外,这个方法不止可以生成新的 retrofit2.Call对象,也可以生成别的类型对象,例如 RxJava 的Obervable,来让 Retrofit 可以和 RxJava 结合使用。

思考:

  1. Retrofit的请求和返回是如何关联的?即如何确保回调到正确的位置?

    答:

    1
    ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object,Object>) loadServiceMethod(method);

    这句话里面绑定了method与对象