【问题标题】:How to implement caching in android app for REST API results?如何在 android 应用程序中为 REST API 结果实现缓存?
【发布时间】:2017-08-18 13:24:37
【问题描述】:

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

}

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

谢谢 阿杰

【问题讨论】:

    标签: android caching android-sdk-2.3


    【解决方案1】:

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

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

    截击示例:technotalkative

    【讨论】:

    【解决方案2】:

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

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

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

    希望这能像对我 Ajay 一样帮助你!

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-07-09
        • 2021-12-05
        • 2020-11-08
        • 2012-04-13
        • 1970-01-01
        • 1970-01-01
        • 2010-10-16
        • 1970-01-01
        相关资源
        最近更新 更多