iis服务器助手广告广告
返回顶部
首页 > 资讯 > 移动开发 >Android中Rxjava实现三级缓存的两种方式
  • 613
分享到

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

rxjava三级缓存缓存Android 2022-06-06 11:06:06 613人浏览 薄情痞子
摘要

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

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

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

源码下载地址:Rxjava_jb51.rar

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

2.使用BehaviorSubject。

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



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.定义一个接口:



public interface ICache {
  <T> Observable<T> get(String key, Class<T> cls);
  <T> void put(String key, T t);
}

2.内存存储的实现



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.磁盘存储的实现



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:



public abstract class NetworkCache<T> {
  public abstract Observable<T> get(String key, final Class<T> cls);
}

6.接下来看怎么使用



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都不用给明,要是有好的实现的方式,欢迎告诉我。

您可能感兴趣的文章:Android图片三级缓存开发浅谈Android 中图片的三级缓存策略Android图片三级缓存的原理及其实现详解Android 图片的三级缓存及图片压缩Android中图片的三级缓存机制Android图片三级缓存策略(网络、本地、内存缓存)Android使用缓存机制实现文件下载及异步请求图片加三级缓存Android实现图片异步请求加三级缓存android中图片的三级缓存cache策略(内存/文件/网络)Android三级缓存原理讲解


--结束END--

本文标题: Android中Rxjava实现三级缓存的两种方式

本文链接: https://www.lsjlt.com/news/28197.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作