17

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

}

如何为缓存对象启用时间?还有 - 什么是最好的序列化方式?即使应用程序关闭并稍后重新打开(如果时间尚未到期),缓存也应该是完整的。

谢谢阿杰

4

3 回答 3

10

现在很棒的库 Volley 在 Google I/O 2013 上发布,它有助于改善调用 REST API 的所有问题:

Volley 是一个库,它是来自 Android 开发团队的名为 Volley 的库。这使得 Android 应用程序的联网更容易,最重要的是,更快。它管理网络请求的处理和缓存,并节省开发人员一次又一次编写相同的网络调用/缓存代码的宝贵时间。更少代码的另一个好处是更少的错误,这就是所有开发人员想要和追求的目标。

截击示例:technotalkative

于 2013-08-16T07:07:01.153 回答
4

最好的方法之一是使用 Matthias Käppler 的 ignited 库来发出将响应缓存在内存(弱引用)和文件中的 http 请求。它真的可以配置做一个或另一个或两者。

该库位于此处:https ://github.com/mttkay/ignition ,示例位于此处:https ://github.com/mttkay/ignition/wiki/Sample-applications

就个人而言,我喜欢这个库,因为它被称为 Droidfu

希望这对您有帮助,就像对我 Ajay 一样!

于 2012-10-05T12:32:21.773 回答
4

首先检查设备是否从互联网连接。

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();}
于 2017-07-13T17:01:09.293 回答