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

3

18

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 i can reuse? I remember coming across such code sometime back. However I cant find it.

If nothing else works, i will write my own. following is basic structure

public class MyCacheManager {

static Map<String, Object> mycache;

public static Object getData(String cacheid) {
    return mycache.get(cacheid);
}

public static void putData(String cacheid, Object obj, int time) {
    mycache.put(cacheid, obj);
}

}

how do i enable time for cached objects? also - whats the best way to serialize? cache should be intact even if app is closed and reopened later (if time has not expired).

Thanks Ajay

Bootery answered 5/10, 2012 at 12:27 Comment(0)
A
10

Now awesome library Volley released on Google I/O 2013 which helps for improve over all problems of calling REST API:

Volley is a library,it is library called Volley from the Android dev team. that makes networking for Android apps easier and most importantly, faster. It manages the processing and caching of network requests and it saves developers valuable time from writing the same network call/cache code again and again. And one more benefit of having less code is less number of bugs and that’s all developers want and aim for.

Example for volley: technotalkative

Armallas answered 16/8, 2013 at 7:7 Comment(1)
Great! You could also find some good samples of using Volley here : github.com/stormzhang/AndroidVolleyCochran
G
4

One of the best ways is to use Matthias Käppler's ignited librarys to make http requests that caches the responses in memory (weak reference) and on file. Its really configurable to do one or the other or both.

The library is located here : https://github.com/mttkay/ignition with examples located here : https://github.com/mttkay/ignition/wiki/Sample-applications

Personally, I love this lib from when it was called Droidfu

Hope this helps you as much as it did me Ajay!

Growler answered 5/10, 2012 at 12:32 Comment(0)
J
4

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

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

Cache interface

public interface Cache<K, V> {

void put(K key, V value);

V get(K key);

V remove(K key);

void clear();}
Jocasta answered 13/7, 2017 at 17:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.