How to implement caching in android app for REST API results?

前端 未结 3 2157
失恋的感觉
失恋的感觉 2020-12-14 02:52

My android app gets its data using REST API. I want to have client side caching implemented. Do we have any inbuilt classes for this?

if not, is these any code that

3条回答
  •  鱼传尺愫
    2020-12-14 03:51

    First check the device is connected from the internet or not.

    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();
    }}
    

    If device is connected from internet then get the data from API and cache it else get the data from cache.

    public class CacheManager {
    
    Cache 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 getCache() {
        return mCache;
    }
    
    public static class DiskCache implements Cache {
    
        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;
        }
    }}
    

    Create the CacheInterceptor class to cache the network response and handle the errors

    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;
    }}
    

    Now add the this interceptor in OkHttpClient while creating the service using Retrofit.

    public final class ServiceManager {
    private static ServiceManager mServiceManager;
    
    public static ServiceManager get() {
        if (mServiceManager == null) {
            mServiceManager = new ServiceManager();
        }
        return mServiceManager;
    }
    
    public  T createService(Class clazz, CacheManager cacheManager, Reachability reachability) {
        return createService(clazz, HttpUrl.parse(ServiceApiEndpoint.SERVICE_ENDPOINT), cacheManager, reachability);
    }
    
    private  T createService(Class clazz, HttpUrl parse, CacheManager cacheManager, Reachability reachability) {
        Retrofit retrofit = getRetrofit(parse, cacheManager, reachability);
        return retrofit.create(clazz);
    }
    
    public  T createService(Class clazz) {
        return createService(clazz, HttpUrl.parse(ServiceApiEndpoint.SERVICE_ENDPOINT));
    }
    
    private  T createService(Class clazz, HttpUrl parse) {
        Retrofit retrofit = getRetrofit(parse);
        return retrofit.create(clazz);
    }
    
    private  T createService(Class 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();
    }}
    

    Cache interface

    public interface Cache {
    
    void put(K key, V value);
    
    V get(K key);
    
    V remove(K key);
    
    void clear();}
    

提交回复
热议问题