如期而至的Repository篇,内部实现则由Realm、Retrofit,以及内存级LruCache组成。Repository,顾名思义,即仓库,向上层屏蔽了数据来源和内部实现细节,不需要了解货物来源,只需要拿走就行了。
首先,为什么我们需要Repository层呢?一言以蔽之,屏蔽细节。
上层(activity/fragment/presenter)不需要知道数据的细节,来自于网络、数据库,亦或是内存等等。举些例子:
如果这些需求,我们都要实现在View或者Presenter中,就会导致充斥大量数据逻辑,目的不单一,难以维护。而Repository层就是来封装这些逻辑的。
自底向上地,我们需要一个OkHttpClient来设置给Retrofit,这里作为实例,放出一段包含大部分你可能会用到的功能的Client创建代码,可以根据需要进行调整。
private OkHttpClient getClient() {
// log用拦截器
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
// 开发模式记录整个body,否则只记录基本信息如返回200,http协议版本等
if (IS_DEV) {
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
} else {
logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
}
// 如果使用到HTTPS,我们需要创建SSLSocketFactory,并设置到client
SSLSocketFactory sslSocketFactory = null;
try {
// 这里直接创建一个不做证书串验证的TrustManager
final TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}
}
};
// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
sslSocketFactory = sslContext.getSocketFactory();
} catch (Exception e) {
Logger.e(TAG, e.getMessage());
}
return new OkHttpClient.Builder()
// HeadInterceptor实现了Interceptor,用来往Request Header添加一些业务相关数据,如APP版本,token信息
.addInterceptor(new HeadInterceptor())
.addInterceptor(logging)
// 连接超时时间设置
.connectTimeout(10, TimeUnit.SECONDS)
// 读取超时时间设置
.readTimeout(10, TimeUnit.SECONDS)
.sslSocketFactory(sslSocketFactory)
// 信任所有主机名
.hostnameVerifier((hostname, session) -> true)
// 这里我们使用host name作为cookie保存的key
.cookieJar(new CookieJar() {
private final HashMap<HttpUrl, List<Cookie>> cookieStore = new HashMap<>();
@Override
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
cookieStore.put(HttpUrl.parse(url.host()), cookies);
}
@Override
public List<Cookie> loadForRequest(HttpUrl url) {
List<Cookie> cookies = cookieStore.get(HttpUrl.parse(url.host()));
return cookies != null ? cookies : new ArrayList<>();
}
})
.build();
}
public static MrService getInstance() {
if (mInstance == null) {
synchronized (MrService.class) {
if (mInstance == null) {
mInstance = new MrService();
}
}
}
return mInstance;
}
private MrService() {
this(true);
}
private MrService(boolean useRxJava) {
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl(IS_DEV ? API_DEV_URL : API_PRODUCT_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(getClient());
if (useRxJava) {
builder.addCallAdapterFactory(RxJavaCallAdapterFactory.create());
}
mRetrofit = builder.build();
}
对应API请求类如
public interface SystemApi {
...
@FormUrlEncoded
@POST("user/feedback")
Observable<MrResponse> feedback(@Field("content") String content,
@Field("model_name") String modelName,
@Field("system_version") String systemVersion,
@Field("img_keys") List<String> imageKeyList);
}
有时候我们需要做同步请求,比如提供结果给一些第三方库,它们可能需要直接返回对应数据(像我最近碰到的融云….)
这时候就需要创建一个同步的retrofit客户端,其实就是不要去使用RxJava的adapter啦。
public static MrService getSynchronousInstance() {
if (mSyncInstance == null) {
synchronized (MrService.class) {
if (mSyncInstance == null) {
mSyncInstance = new MrService(false);
}
}
}
return mSyncInstance;
}
对应地,我们需要定义请求类,这里我们需要使用Call<>去包一下最终解析对象的类。
public interface RongCloudApi {
@FormUrlEncoded
@POST("im/getGroupInfo")
Call<MrResponse> getGroupInfoSynchronous(@Field("group_id") String groupId);
@FormUrlEncoded
@POST("user/nameCardLite")
Call<MrResponse> getNameCardLiteSynchronous(@Field("uid") String userId);
}