优雅地烘焙 Retrofit

前言

将构造 Retrofit 时所需要的材料隔离开来,利用依赖倒置这个原则,优雅地烘烤出美味的 Retrofit 实例。

类图

来自《Head First 设计模式》抽象工厂一节中披萨店卖披萨的启发,我们也将 Retrofit 看做一样商品,来设计它。

依赖倒置

RetrofitWrapper

我们需要的产品,内部包含了一个 Retrofit 的引用。我们将这个类抽象,构造 Retrofit 的原料来自另一个抽象类:RetrofitWrapperFactory,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 产品类,包装了一个 Retrofit
*/
public abstract class RetrofitWrapper {
protected Retrofit mRetrofit;
public RetrofitWrapper(RetrofitWrapperFactory retrofitWrapperFactory) {
mRetrofit = new Retrofit.Builder().baseUrl(retrofitWrapperFactory.provideBaseUrl())
.client(retrofitWrapperFactory.createOkHttpClient()) // 提供 OkHttp 实例
.addConverterFactory(retrofitWrapperFactory.createGsonConverterFactory()) // 提供 GsonConvertFactory 实例
.addCallAdapterFactory(retrofitWrapperFactory.createRxJavaCallAdapter()) // 提供RxJavaCallAdapter 的实例
.build();
}
public Retrofit getRetrofit() {
return mRetrofit;
}
}

RetrofitWrapperFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* RetrofitWrapper 抽象工厂,提供 RetrofitWrapper 需要的原料
*/
public interface RetrofitWrapperFactory {
String provideBaseUrl();
OkHttpClient createOkHttpClient();
Converter.Factory createGsonConverterFactory();
CallAdapter.Factory createRxJavaCallAdapter();
}

RetrofitWrapperStore

兜售 RetrofitWrapper 的商店,这里商店类还继承了一个 RetrofitApiProvider 接口,用来对外暴露创建 Retrofit Api Service 的接口。

1
2
3
4
5
6
7
8
/**
* RetrofitWrapper 商店类
*/
public abstract class RetrofitWrapperStore implements RetrofitApiProvider {
protected abstract RetrofitWrapper createRetrofitWrapper();
}
1
2
3
4
5
public interface RetrofitApiProvider {
<T> T provideApi(Class<T> service);
}

举栗时间

具体产品类

1
2
3
4
5
6
7
public class PeroRetrofitWrapper extends RetrofitWrapper {
public PeroRetrofitWrapper(RetrofitWrapperFactory retrofitWrapperFactory) {
super(retrofitWrapperFactory);
}
}

具体工厂类

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
public class PeroRetrofitWrapperFactory implements RetrofitWrapperFactory {
private static final int DEFAULT_CONNECT_TIME_OUT = 10; // 默认超时时间
private static final String TEST_SERVER_URL = "http://192.168.199.182:3000";
@Override public OkHttpClient createOkHttpClient() {
return new OkHttpClient.Builder().connectTimeout(DEFAULT_CONNECT_TIME_OUT, TimeUnit.SECONDS)
.addInterceptor(provideHttpLoggingInterceptor())
.addNetworkInterceptor(provideTokenInterceptor())
.build();
}
private Interceptor provideHttpLoggingInterceptor() {
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
return httpLoggingInterceptor;
}
private Interceptor provideTokenInterceptor() {
return new TokenInterceptor();
}
@Override public CallAdapter.Factory createRxJavaCallAdapter() {
return RxJavaCallAdapterFactory.create();
}
@Override public Converter.Factory createGsonConverterFactory() {
Gson gson =
new GsonBuilder().registerTypeAdapterFactory(provideParamsAdapterFactory()).create();
return GsonConverterFactory.create(gson);
}
private TypeAdapterFactory provideParamsAdapterFactory() {
return new ParamsAdapterFactory();
}
@Override public String provideBaseUrl() {
return TEST_SERVER_URL;
}
}

具体商店类

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
public final class PeroRetrofitWrapperStore extends RetrofitWrapperStore {
private RetrofitWrapper mRetrofitWrapper;
public PeroRetrofitWrapperStore() {
mRetrofitWrapper = createRetrofitWrapper();
}
private static class SingletonHolder {
private static final PeroRetrofitWrapperStore sInstance = new PeroRetrofitWrapperStore();
}
public static PeroRetrofitWrapperStore getInstance() {
return SingletonHolder.sInstance;
}
@Override protected RetrofitWrapper createRetrofitWrapper() {
return new PeroRetrofitWrapper(new PeroRetrofitWrapperFactory());
}
@Override public <T> T provideApi(Class<T> service) {
return mRetrofitWrapper.getRetrofit().create(service);
}
}

如何调用?

在实际项目中,PeroRetrofitWrapperPeroRetrofitWrapperFactory 可以作为 PeroRetrofitWrapperStore 的静态内部类,从而屏蔽外部可见性。毕竟,现实生活中,我们买披萨也是去商店购买,而不会跑到原料工厂自己动手造披萨对吧2333。

1
2
3
4
5
6
7
8
// 获取手机验证码
private void fetchPinCode(final String phoneNum) {
PeroRetrofitWrapperStore.getInstance()
.provideApi(PeroApi.UserApi.class)
.fetchSmsCode(new Params.Builder().put("phoneNumber", phoneNum).create())
// 省略...

思考&改进

示例代码里已经将 RetrofitWrapper 单例化了,而创建 Service Api,其实还是在 new object

1
2
3
4
5
@Override public <T> T provideApi(Class<T> service) {
return mRetrofitWrapper.getRetrofit().create(service);
}

如果有需要,也可以将 Api Service 进行单例化。

总结

new 关键字的地方我们就应该谨慎,是否耦合得太厉害,是否可以用工厂模式来解耦。当然,因为我的项目里暂时还没有 Dagger2,所以自己动手,实现解耦,如果有 Dagger2 的话,就不用这么麻烦了233。