首先检查设备是否从互联网连接。
public class Reachability {
private final ConnectivityManager mConnectivityManager;
public Reachability(Context context) {
mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
}
public boolean isConnected() {
NetworkInfo networkInfo = mConnectivityManager.getActiveNetworkInfo();
return networkInfo != null && networkInfo.isConnectedOrConnecting();
}}
如果设备是从互联网连接的,则从 API 获取数据并缓存它,否则从缓存中获取数据。
public class CacheManager {
Cache<String, String> mCache;
private DiskLruCache mDiskLruCache;
private final Context mContext;
public CacheManager(Context context) throws IOException {
mContext = context;
setUp();
mCache = DiskCache.getInstanceUsingDoubleLocking(mDiskLruCache);
}
public void setUp() throws IOException {
File cacheInFiles = mContext.getFilesDir();
int version = BuildConfig.VERSION_CODE;
int KB = 1024;
int MB = 1024 * KB;
int cacheSize = 400 * MB;
mDiskLruCache = DiskLruCache.open(cacheInFiles, version, 1, cacheSize);
}
public Cache<String, String> getCache() {
return mCache;
}
public static class DiskCache implements Cache<String, String> {
private static DiskLruCache mDiskLruCache;
private static DiskCache instance = null;
public static DiskCache getInstanceUsingDoubleLocking(DiskLruCache diskLruCache){
mDiskLruCache = diskLruCache;
if(instance == null){
synchronized (DiskCache.class) {
if(instance == null){
instance = new DiskCache();
}
}
}
return instance;
}
@Override
public synchronized void put(String key, String value) {
try {
if (mDiskLruCache != null) {
DiskLruCache.Editor edit = mDiskLruCache.edit(getMd5Hash(key));
if (edit != null) {
edit.set(0, value);
edit.commit();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public synchronized String get(String key) {
try {
if (mDiskLruCache != null) {
DiskLruCache.Snapshot snapshot = mDiskLruCache.get(getMd5Hash(key));
if (snapshot == null) {
// if there is a cache miss simply return null;
return null;
}
return snapshot.getString(0);
}
} catch (IOException e) {
e.printStackTrace();
}
// in case of error in reading return null;
return null;
}
@Override
public String remove(String key) {
// TODO: implement
return null;
}
@Override
public void clear() {
// TODO: implement
}
}
public static String getMd5Hash(String input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] messageDigest = md.digest(input.getBytes());
BigInteger number = new BigInteger(1, messageDigest);
String md5 = number.toString(16);
while (md5.length() < 32)
md5 = "0" + md5;
return md5;
} catch (NoSuchAlgorithmException e) {
Log.e("MD5", e.getLocalizedMessage());
return null;
}
}}
创建 CacheInterceptor 类来缓存网络响应并处理错误
public class CacheInterceptor implements Interceptor{
private final CacheManager mCacheManager;
private final Reachability mReachability;
public CacheInterceptor(CacheManager cacheManager, Reachability reachability) {
mCacheManager = cacheManager;
mReachability = reachability;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
String key = request.url().toString();
Response response;
if (mReachability.isConnected()) {
try {
response = chain.proceed(request);
Response newResponse = response.newBuilder().build();
if (response.isSuccessful()) {
if (response.code() == 204) {
return response;
}
// save to cache this success model.
mCacheManager.getCache().put(key, newResponse.body().string());
// now we know that we definitely have a cache hit.
return getCachedResponse(key, request);
}else if (response.code() >= 500) { // accommodate all server errors
// check if there is a cache hit or miss.
if (isCacheHit(key)) {
// if data is in cache, the return the data from cache.
return getCachedResponse(key, request);
}else {
// if it's a miss, we can't do much but return the server state.
return response;
}
}else { // if there is any client side error
// forward the response as it is to the business layers to handle.
return response;
}
} catch (ConnectException | UnknownHostException e) {
// Internet connection exception.
e.printStackTrace();
}
}
// if somehow there is an internet connection error
// check if the data is already cached.
if (isCacheHit(key)) {
return getCachedResponse(key, request);
}else {
// if the data is not in the cache we'll throw an internet connection error.
throw new UnknownHostException();
}
}
private Response getCachedResponse(String url, Request request) {
String cachedData = mCacheManager.getCache().get(url);
return new Response.Builder().code(200)
.body(ResponseBody.create(MediaType.parse("application/json"), cachedData))
.request(request)
.protocol(Protocol.HTTP_1_1)
.build();
}
public boolean isCacheHit(String key) {
return mCacheManager.getCache().get(key) != null;
}}
现在在 OkHttpClient 中添加 this 拦截器,同时使用 Retrofit 创建服务。
public final class ServiceManager {
private static ServiceManager mServiceManager;
public static ServiceManager get() {
if (mServiceManager == null) {
mServiceManager = new ServiceManager();
}
return mServiceManager;
}
public <T> T createService(Class<T> clazz, CacheManager cacheManager, Reachability reachability) {
return createService(clazz, HttpUrl.parse(ServiceApiEndpoint.SERVICE_ENDPOINT), cacheManager, reachability);
}
private <T> T createService(Class<T> clazz, HttpUrl parse, CacheManager cacheManager, Reachability reachability) {
Retrofit retrofit = getRetrofit(parse, cacheManager, reachability);
return retrofit.create(clazz);
}
public <T> T createService(Class<T> clazz) {
return createService(clazz, HttpUrl.parse(ServiceApiEndpoint.SERVICE_ENDPOINT));
}
private <T> T createService(Class<T> clazz, HttpUrl parse) {
Retrofit retrofit = getRetrofit(parse);
return retrofit.create(clazz);
}
private <T> T createService(Class<T> clazz, Retrofit retrofit) {
return retrofit.create(clazz);
}
private Retrofit getRetrofit(HttpUrl httpUrl, CacheManager cacheManager, Reachability reachability) {
return new Retrofit.Builder()
.baseUrl(httpUrl)
.client(createClient(cacheManager, reachability))
.addConverterFactory(getConverterFactory())
.build();
}
private OkHttpClient createClient(CacheManager cacheManager, Reachability reachability) {
return new OkHttpClient.Builder().addInterceptor(new CacheInterceptor(cacheManager, reachability)).build();
}
private Retrofit getRetrofit(HttpUrl parse) {
return new Retrofit.Builder()
.baseUrl(parse)
.client(createClient())
.addConverterFactory(getConverterFactory()).build();
}
private Retrofit getPlainRetrofit(HttpUrl httpUrl) {
return new Retrofit.Builder()
.baseUrl(httpUrl)
.client(new OkHttpClient.Builder().build())
.addConverterFactory(getConverterFactory())
.build();
}
private Converter.Factory getConverterFactory() {
return GsonConverterFactory.create();
}
private OkHttpClient createClient() {
return new OkHttpClient.Builder().build();
}}
缓存接口
public interface Cache<K, V> {
void put(K key, V value);
V get(K key);
V remove(K key);
void clear();}