Как реализовать кеширование в приложении Android для результатов REST API?

Мое приложение для Android получает данные с помощью REST API. Я хочу реализовать кэширование на стороне клиента. Есть ли у нас встроенные классы для этого?

если нет, это любой код, который я могу повторно использовать? Я помню, как когда-то сталкивался с таким кодом. Однако я не могу найти его.

Если ничего не работает, я напишу свой собственный. Ниже приведена основная структура

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

}

как включить время для кешированных объектов? также - какой лучший способ сериализации? кеш должен быть неповрежденным, даже если приложение закрыто и открыто позже (если время не истекло).

Спасибо, Аджай.


person Thoughtful Monkey    schedule 05.10.2012    source источник


Ответы (3)


Теперь на Google I/O 2013 выпущена потрясающая библиотека Volley, которая помогает решить все проблемы с вызовом REST API:

Volley — это библиотека, это библиотека под названием Volley от команды разработчиков Android. . это делает работу в сети для Android-приложений проще и, самое главное, быстрее. Он управляет обработкой и кешированием сетевых запросов и экономит разработчикам драгоценное время от написания одного и того же кода сетевого вызова/кеша снова и снова. И еще одно преимущество меньшего количества кода — меньшее количество ошибок, и это все, чего разработчики хотят и к чему стремятся.

Пример залпа: technotalkative

person LOG_TAG    schedule 16.08.2013
comment
Здорово! Вы также можете найти несколько хороших примеров использования Volley здесь: github.com/stormzhang/AndroidVolley - person Sam003; 16.07.2015

Один из лучших способов — использовать зажженные библиотеки Matthias Käppler для выполнения HTTP-запросов, кэширующих ответы в памяти (слабая ссылка) и в файле. Его действительно можно настроить, чтобы сделать одно или другое или оба.

Библиотека находится здесь: https://github.com/mttkay/ignition с примерами здесь: https://github.com/mttkay/ignition/wiki/Sample-applications

Лично я люблю эту библиотеку, когда она называлась Droidfu.

Надеюсь, это поможет вам так же, как и мне, Аджай!

person petey    schedule 05.10.2012

Сначала проверьте, подключено ли устройство из Интернета или нет.

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 при создании службы с помощью 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();}
person Tanvi Agarwal    schedule 13.07.2017