全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

Android中Rxjava实现三级缓存的两种方式

本文正如标题所说的用rxjava实现数据的三级缓存分别为内存,磁盘,网络,刚好最近在看Android源码设计模式解析与实战(受里面的ImageLoader的设计启发)。

我把代码放到了我的hot项目中,github地址

源码下载地址:Rxjava_jb51.rar

1.使用concat()和first()的操作符。

2.使用BehaviorSubject。

先说BehaviorSubject的实现方法,废话不多说直接上代码,

/**
 * Created by wukewei on 16/6/20.
 */
public class BehaviorSubjectFragment extends BaseFragment {

  public static BehaviorSubjectFragment newInstance() {
    BehaviorSubjectFragment fragment = new BehaviorSubjectFragment();
    return fragment;
  }

  String diskData = null;
  String networkData = "从服务器获取的数据";
  BehaviorSubject<String> cache;

  View mView;

  @Nullable
  @Override
  public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    mView = inflater.inflate(R.layout.fragment_content, container, false);
    init();
    return mView;
  }

  private void init() {
    mView.findViewById(R.id.get).setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        subscriptionData(new Observer<String>() {
          @Override
          public void onCompleted() {

          }

          @Override
          public void onError(Throwable e) {

          }

          @Override
          public void onNext(String s) {
            Log.d("onNext", s);
          }
        });
      }
    });

    mView.findViewById(R.id.memory).setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        BehaviorSubjectFragment.this.cache = null;
      }
    });

    mView.findViewById(R.id.memory_disk).setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        BehaviorSubjectFragment.this.cache = null;
        BehaviorSubjectFragment.this.diskData = null;
      }
    });

  }

  private void loadNewWork() {
    Observable<String> o = Observable.just(networkData)
        .doOnNext(new Action1<String>() {
          @Override
          public void call(String s) {
            BehaviorSubjectFragment.this.diskData = s;
            Log.d("写入磁盘", "写入磁盘");
          }
        });
    o.subscribe(new Action1<String>() {
      @Override
      public void call(String s) {
        cache.onNext(s);
      }
    }, new Action1<Throwable>() {
      @Override
      public void call(Throwable throwable) {

      }
    });
  }

  private Subscription subscriptionData(@NonNull Observer<String> observer) {
    if (cache == null) {
      cache = BehaviorSubject.create();
      Observable.create(new Observable.OnSubscribe<String>() {
        @Override
        public void call(Subscriber<? super String> subscriber) {
          String data = diskData;
          if (data == null) {
            Log.d("来自网络", "来自网络");
            loadNewWork();
          } else {
            Log.d("来自磁盘", "来自磁盘");
            subscriber.onNext(data);
          }
        }
      })
          .subscribeOn(Schedulers.io())
          .subscribe(cache);

    } else {
      Log.d("来自内存", "来自内存");
    }

    return cache.observeOn(AndroidSchedulers.mainThread()).subscribe(observer);
  }


}

其中最主要的是subscriptionData()这个方法,就是先判断 cache是否存在要是存在的话就返回内存中数据,再去判断磁盘数据是否存在,如果存在就返回,要是前面两种都不存在的时候,再去网络中获取数据。还有最重要的是当你从网络获取数据的时候要记得保存在内存中和保存在磁盘中,在磁盘获取数据的时候把它赋值给内存。

接下来就说说用concat()和first()的操作符来实现,这是我在看Android源码设计模式解析与实战,作者在第一章的时候就介绍ImageLoader的设计。

在内存中存储的方式LruCache来实现的,磁盘存储的方式就是序列化存储。

1.定义一个接口:

/**
 * Created by wukewei on 16/6/19.
 */
public interface ICache {
  <T> Observable<T> get(String key, Class<T> cls);

  <T> void put(String key, T t);
}

2.内存存储的实现

/**
 * Created by wukewei on 16/6/19.
 */
public class MemoryCache implements ICache{

  private LruCache<String, String> mCache;

  public MemoryCache() {
    final int maxMemory = (int) Runtime.getRuntime().maxMemory();
    final int cacheSize = maxMemory / 8;
    mCache = new LruCache<String, String>(cacheSize) {
      @Override
      protected int sizeOf(String key, String value) {
        try {
          return value.getBytes("UTF-8").length;
        } catch (UnsupportedEncodingException e) {
          e.printStackTrace();
          return value.getBytes().length;
        }
      }
    };
  }

  @Override
  public <T> Observable<T> get(final String key, final Class<T> cls) {
    return Observable.create(new Observable.OnSubscribe<T>() {
      @Override
      public void call(Subscriber<? super T> subscriber) {

        String result = mCache.get(key);

        if (subscriber.isUnsubscribed()) {
          return;
        }

        if (TextUtils.isEmpty(result)) {
          subscriber.onNext(null);
        } else {
          T t = new Gson().fromJson(result, cls);
          subscriber.onNext(t);
        }

        subscriber.onCompleted();
      }
    });
  }

  @Override
  public <T> void put(String key, T t) {
    if (null != t) {
      mCache.put(key, new Gson().toJson(t));
    }
  }

  public void clearMemory(String key) {
    mCache.remove(key);
  }
}

3.磁盘存储的实现

/**
 * Created by wukewei on 16/6/19.
 */
public class DiskCache implements ICache{

  private static final String NAME = ".db";
  public static long OTHER_CACHE_TIME = 10 * 60 * 1000;
  public static long WIFI_CACHE_TIME = 30 * 60 * 1000;
  File fileDir;
  public DiskCache() {
    fileDir = CacheLoader.getApplication().getCacheDir();
  }

  @Override
  public <T> Observable<T> get(final String key, final Class<T> cls) {
    return Observable.create(new Observable.OnSubscribe<T>() {
      @Override
      public void call(Subscriber<? super T> subscriber) {

        T t = (T) getDiskData1(key + NAME);

        if (subscriber.isUnsubscribed()) {
          return;
        }

        if (t == null) {
          subscriber.onNext(null);
        } else {
          subscriber.onNext(t);
        }

        subscriber.onCompleted();
      }
    })
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread());

  }

  @Override
  public <T> void put(final String key, final T t) {
    Observable.create(new Observable.OnSubscribe<T>() {
      @Override
      public void call(Subscriber<? super T> subscriber) {

        boolean isSuccess = isSave(key + NAME, t);

        if (!subscriber.isUnsubscribed() && isSuccess) {

          subscriber.onNext(t);
          subscriber.onCompleted();
        }
      }
    })
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe();
  }

  /**
   * 保存数据
   */
  private <T> boolean isSave(String fileName, T t) {
    File file = new File(fileDir, fileName);

    ObjectOutputStream objectOut = null;
    boolean isSuccess = false;
    try {
      FileOutputStream out = new FileOutputStream(file);
          objectOut = new ObjectOutputStream(out);
      objectOut.writeObject(t);
      objectOut.flush();
      isSuccess=true;
    } catch (IOException e) {
      Log.e("写入缓存错误",e.getMessage());
    } catch (Exception e) {
      Log.e("写入缓存错误",e.getMessage());
    } finally {
      closeSilently(objectOut);
    }
    return isSuccess;
  }

  /**
   * 获取保存的数据
   */
  private Object getDiskData1(String fileName) {
    File file = new File(fileDir, fileName);

    if (isCacheDataFailure(file)) {
      return null;
    }

    if (!file.exists()) {
      return null;
    }
    Object o = null;
    ObjectInputStream read = null;
    try {
      read = new ObjectInputStream(new FileInputStream(file));
      o = read.readObject();
    } catch (StreamCorruptedException e) {
      Log.e("读取错误", e.getMessage());
    } catch (IOException e) {
      Log.e("读取错误", e.getMessage());
    } catch (ClassNotFoundException e) {
      Log.e("错误", e.getMessage());
    } finally {
      closeSilently(read);
    }
    return o;
  }



  private void closeSilently(Closeable closeable) {
    if (closeable != null) {
      try {
        closeable.close();
      } catch (Exception ignored) {
      }
    }
  }



  /**
   * 判断缓存是否已经失效
   */
  private boolean isCacheDataFailure(File dataFile) {
    if (!dataFile.exists()) {
      return false;
    }
    long existTime = System.currentTimeMillis() - dataFile.lastModified();
    boolean failure = false;
    if (NetWorkUtil.getNetworkType(CacheLoader.getApplication()) == NetWorkUtil.NETTYPE_WIFI) {
      failure = existTime > WIFI_CACHE_TIME ? true : false;
    } else {
      failure = existTime > OTHER_CACHE_TIME ? true : false;
    }

    return failure;
  }

  public void clearDisk(String key) {
    File file = new File(fileDir, key + NAME);
    if (file.exists()) file.delete();
  }
}

isCacheDataFailure()方式中就是判断当前的数据是否失效,我是根据当前的网络状况来分wifi状况和非wifi状况,wifi状态下数据过期时间比较短,其他状态过期时间比较长。

4.CacheLoader的设计

/
**
 * Created by wukewei on 16/6/19.
 */
public class CacheLoader {
  private static Application application;

  public static Application getApplication() {
    return application;
  }

  private ICache mMemoryCache, mDiskCache;

  private CacheLoader() {

    mMemoryCache = new MemoryCache();
    mDiskCache = new DiskCache();
  }
  private static CacheLoader loader;

  public static CacheLoader getInstance(Context context) {
    application = (Application) context.getApplicationContext();
    if (loader == null) {
      synchronized (CacheLoader.class) {
        if (loader == null) {
          loader = new CacheLoader();
        }
      }
    }
    return loader;
  }


  public <T> Observable<T> asDataObservable(String key, Class<T> cls, NetworkCache<T> networkCache) {

    Observable observable = Observable.concat(
        memory(key, cls),
        disk(key, cls),
        network(key, cls, networkCache))
        .first(new Func1<T, Boolean>() {
          @Override
          public Boolean call(T t) {
            return t != null;
          }
        });
    return observable;
  }

  private <T> Observable<T> memory(String key, Class<T> cls) {

    return mMemoryCache.get(key, cls).doOnNext(new Action1<T>() {
      @Override
      public void call(T t) {
        if (null != t) {
          Log.d("我是来自内存","我是来自内存");
        }
      }
    });
  }

  private <T> Observable<T> disk(final String key, Class<T> cls) {

    return mDiskCache.get(key, cls)
        .doOnNext(new Action1<T>() {
          @Override
          public void call(T t) {
            if (null != t) {
              Log.d("我是来自磁盘","我是来自磁盘");
              mMemoryCache.put(key, t);
            }
          }
        });
  }

  private <T> Observable<T> network(final String key, Class<T> cls
      , NetworkCache<T> networkCache) {

    return networkCache.get(key, cls)
        .doOnNext(new Action1<T>() {
          @Override
          public void call(T t) {
            if (null != t) {
              Log.d("我是来自网络","我是来自网络");
              mDiskCache.put(key, t);
              mMemoryCache.put(key, t);
            }
          }
        });
  }


  public void clearMemory(String key) {
    ((MemoryCache)mMemoryCache).clearMemory(key);
  }



  public void clearMemoryDisk(String key) {
    ((MemoryCache)mMemoryCache).clearMemory(key);
    ((DiskCache)mDiskCache).clearDisk(key);
  }
}

5.网络获取的NetworkCache:

/**
 * Created by wukewei on 16/6/19.
 */
public abstract class NetworkCache<T> {
  public abstract Observable<T> get(String key, final Class<T> cls);
}

6.接下来看怎么使用

/**
 * Created by wukewei on 16/5/30.
 */
public class ItemPresenter extends BasePresenter<ItemContract.View> implements ItemContract.Presenter {

  private static final String key = "new_list";
  protected int pn = 1;

  protected void replacePn() {
    pn = 1;
  }

  private boolean isRefresh() {
    return pn == 1;
  }

  private NetworkCache<ListPopular> networkCache;


  public ItemPresenter(Activity activity, ItemContract.View view) {
    super(activity, view);

  }

  @Override
  public void getListData(String type) {
    if (isRefresh()) mView.showLoading();
    networkCache = new NetworkCache<ListPopular>() {
      @Override
      public Observable<ListPopular> get(String key, Class<ListPopular> cls) {
        return mHotApi.getPopular(ItemPresenter.this.pn, Constants.PAGE_SIZE, type)
            .compose(SchedulersCompat.applyIoSchedulers())
            .compose(RxResultHelper.handleResult())
            .flatMap(populars -> {
              ListPopular popular = new ListPopular(populars);
              return Observable.just(popular);
            });
      }
    };

    Subscription subscription = CacheLoader.getInstance(mActivity)
        .asDataObservable(key + type + ItemPresenter.this.pn, ListPopular.class, networkCache)
        .map(listPopular -> listPopular.data)
        .subscribe(populars -> {
          mView.showContent();
          if (isRefresh()) {
            if (populars.size() == 0) mView.showNotdata();
            mView.addRefreshData(populars);
          } else {
            mView.addLoadMoreData(populars);
          }
        }, throwable -> {
          if (isRefresh())
          mView.showError(ErrorHanding.handleError(throwable));
          handleError(throwable);
        });

    addSubscrebe(subscription);

  }
}

一定要给个key,我是根据key来获取数据的,还要就是给个类型。

但是这个我设计的这个缓存还是不是很理想,接来下想要实现的就是在传入的时候类的class都不用给明,要是有好的实现的方式,欢迎告诉我。


# rxjava  # 三级缓存  # android  # 缓存  # Android Rxjava3 使用场景详解  # android使用Rxjava实现倒计时功能  # Android 使用 RxJava2 实现倒计时功能的示例代码  # Android RxJava创建操作符Timer的方法  # Android RxJava创建操作符Interval  # Android Retrofit和Rxjava的网络请求  # android使用RxJava实现预加载  # Android RxJava异步数据处理库使用详解  # 我是  # 再去  # 来实现  # 给个  # 是否存在  # 的是  # 这是  # 我在  # 都不  # 告诉我  # 下载地址  # 两种  # 当你  # 不是很  # 把它  # 我把  # 分别为  # 最主要  # 多说  # 最重要的是 


相关文章: 建站之星如何助力网站排名飙升?揭秘高效技巧  如何在阿里云购买域名并搭建网站?  深圳防火门网站制作公司,深圳中天明防火门怎么编码?  javascript基本数据类型及类型检测常用方法小结  简易网站制作视频教程,使用记事本编写一个简单的网页html文件?  如何通过VPS搭建网站快速盈利?  如何快速搭建个人网站并优化SEO?  小说建站VPS选用指南:性能对比、配置优化与建站方案解析  香港服务器WordPress建站指南:SEO优化与高效部署策略  建站三合一如何选?哪家性价比更高?  高端建站如何打造兼具美学与转化的品牌官网?  合肥做个网站多少钱,合肥本地有没有比较靠谱的交友平台?  如何在IIS中配置站点IP、端口及主机头?  c++怎么使用类型萃取type_traits_c++ 模板元编程类型判断【方法】  已有域名和空间如何搭建网站?  网站制作培训多少钱一个月,网站优化seo培训课程有哪些?  济南网站制作的价格,历城一职专官方网站?  网站视频怎么制作,哪个网站可以免费收看好莱坞经典大片?  建站之星下载版如何获取与安装?  网站制作专业公司有哪些,如何制作一个企业网站,建设网站的基本步骤有哪些?  制作网页的网站有哪些,电脑上怎么做网页?  如何选择高效可靠的多用户建站源码资源?  制作证书网站有哪些,全国城建培训中心证书查询官网?  在线流程图制作网站手机版,谁能推荐几个好的CG原画资源网站么?  深圳网站制作平台,深圳市做网站好的公司有哪些?  ,有什么在线背英语单词效率比较高的网站?  建站之星CMS五站合一模板配置与SEO优化指南  北京网站制作的公司有哪些,北京白云观官方网站?  深圳网站制作案例,网页的相关名词有哪些?  如何在腾讯云服务器快速搭建个人网站?  无锡制作网站公司有哪些,无锡优八网络科技有限公司介绍?  招贴海报怎么做,什么是海报招贴?  nginx修改上传文件大小限制的方法  文字头像制作网站推荐软件,醒图能自动配文字吗?  昆明网站制作哪家好,昆明公租房申请网上登录入口?  开心动漫网站制作软件下载,十分开心动画为何停播?  建站之家VIP精选网站模板与SEO优化教程整合指南  建站主机类型有哪些?如何正确选型  网站制作服务平台,有什么网站可以发布本地服务信息?  贸易公司网站制作流程,出口贸易网站设计怎么做?  深圳企业网站制作设计,在深圳如何网上全流程注册公司?  ,想在网上投简历,哪几个网站比较好?  ui设计制作网站有哪些,手机UI设计网址吗?  制作公司内部网站有哪些,内网如何建网站?  c++怎么编写动态链接库dll_c++ __declspec(dllexport)导出与调用【方法】  大同网页,大同瑞慈医院官网?  如何高效利用亚马逊云主机搭建企业网站?  如何快速查询网址的建站时间与历史轨迹?  南宁网站建设制作定制,南宁网站建设可以定制吗?  C++如何使用std::optional?(处理可选值) 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。