Android中Rxjava实现三级缓存的两种方式
本文正如标题所说的用rxjava实现数据的三级缓存分别为内存,磁盘,网络,刚好最近在看Android源码设计模式解析与实战(受里面的ImageLoader的设计启发)。
我把代码放到了我的hot项目中,github地址
源码下载地址:Rxjava_jb51.rar
1.使用concat()和first()的操作符。
2.使用BehaviorSubject。
先说BehaviorSubject的实现方法,废话不多说直接上代码,
/** *Createdbywukeweion16/6/20. */ publicclassBehaviorSubjectFragmentextendsBaseFragment{ publicstaticBehaviorSubjectFragmentnewInstance(){ BehaviorSubjectFragmentfragment=newBehaviorSubjectFragment(); returnfragment; } StringdiskData=null; StringnetworkData="从服务器获取的数据"; BehaviorSubjectcache; ViewmView; @Nullable @Override publicViewonCreateView(LayoutInflaterinflater,@NullableViewGroupcontainer,@NullableBundlesavedInstanceState){ mView=inflater.inflate(R.layout.fragment_content,container,false); init(); returnmView; } privatevoidinit(){ mView.findViewById(R.id.get).setOnClickListener(newView.OnClickListener(){ @Override publicvoidonClick(Viewv){ subscriptionData(newObserver (){ @Override publicvoidonCompleted(){ } @Override publicvoidonError(Throwablee){ } @Override publicvoidonNext(Strings){ Log.d("onNext",s); } }); } }); mView.findViewById(R.id.memory).setOnClickListener(newView.OnClickListener(){ @Override publicvoidonClick(Viewv){ BehaviorSubjectFragment.this.cache=null; } }); mView.findViewById(R.id.memory_disk).setOnClickListener(newView.OnClickListener(){ @Override publicvoidonClick(Viewv){ BehaviorSubjectFragment.this.cache=null; BehaviorSubjectFragment.this.diskData=null; } }); } privatevoidloadNewWork(){ Observable o=Observable.just(networkData) .doOnNext(newAction1 (){ @Override publicvoidcall(Strings){ BehaviorSubjectFragment.this.diskData=s; Log.d("写入磁盘","写入磁盘"); } }); o.subscribe(newAction1 (){ @Override publicvoidcall(Strings){ cache.onNext(s); } },newAction1 (){ @Override publicvoidcall(Throwablethrowable){ } }); } privateSubscriptionsubscriptionData(@NonNullObserver observer){ if(cache==null){ cache=BehaviorSubject.create(); Observable.create(newObservable.OnSubscribe (){ @Override publicvoidcall(Subscribersubscriber){ Stringdata=diskData; if(data==null){ Log.d("来自网络","来自网络"); loadNewWork(); }else{ Log.d("来自磁盘","来自磁盘"); subscriber.onNext(data); } } }) .subscribeOn(Schedulers.io()) .subscribe(cache); }else{ Log.d("来自内存","来自内存"); } returncache.observeOn(AndroidSchedulers.mainThread()).subscribe(observer); } }
其中最主要的是subscriptionData()这个方法,就是先判断cache是否存在要是存在的话就返回内存中数据,再去判断磁盘数据是否存在,如果存在就返回,要是前面两种都不存在的时候,再去网络中获取数据。还有最重要的是当你从网络获取数据的时候要记得保存在内存中和保存在磁盘中,在磁盘获取数据的时候把它赋值给内存。
接下来就说说用concat()和first()的操作符来实现,这是我在看Android源码设计模式解析与实战,作者在第一章的时候就介绍ImageLoader的设计。
在内存中存储的方式LruCache来实现的,磁盘存储的方式就是序列化存储。
1.定义一个接口:
/** *Createdbywukeweion16/6/19. */ publicinterfaceICache{Observable get(Stringkey,Class cls); voidput(Stringkey,Tt); }
2.内存存储的实现
/** *Createdbywukeweion16/6/19. */ publicclassMemoryCacheimplementsICache{ privateLruCachemCache; publicMemoryCache(){ finalintmaxMemory=(int)Runtime.getRuntime().maxMemory(); finalintcacheSize=maxMemory/8; mCache=newLruCache (cacheSize){ @Override protectedintsizeOf(Stringkey,Stringvalue){ try{ returnvalue.getBytes("UTF-8").length; }catch(UnsupportedEncodingExceptione){ e.printStackTrace(); returnvalue.getBytes().length; } } }; } @Override public Observable get(finalStringkey,finalClass cls){ returnObservable.create(newObservable.OnSubscribe (){ @Override publicvoidcall(Subscribersubscriber){ Stringresult=mCache.get(key); if(subscriber.isUnsubscribed()){ return; } if(TextUtils.isEmpty(result)){ subscriber.onNext(null); }else{ Tt=newGson().fromJson(result,cls); subscriber.onNext(t); } subscriber.onCompleted(); } }); } @Override public voidput(Stringkey,Tt){ if(null!=t){ mCache.put(key,newGson().toJson(t)); } } publicvoidclearMemory(Stringkey){ mCache.remove(key); } }
3.磁盘存储的实现
/** *Createdbywukeweion16/6/19. */ publicclassDiskCacheimplementsICache{ privatestaticfinalStringNAME=".db"; publicstaticlongOTHER_CACHE_TIME=10*60*1000; publicstaticlongWIFI_CACHE_TIME=30*60*1000; FilefileDir; publicDiskCache(){ fileDir=CacheLoader.getApplication().getCacheDir(); } @Override publicObservable get(finalStringkey,finalClass cls){ returnObservable.create(newObservable.OnSubscribe (){ @Override publicvoidcall(Subscribersubscriber){ Tt=(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 voidput(finalStringkey,finalTt){ Observable.create(newObservable.OnSubscribe (){ @Override publicvoidcall(Subscribersubscriber){ booleanisSuccess=isSave(key+NAME,t); if(!subscriber.isUnsubscribed()&&isSuccess){ subscriber.onNext(t); subscriber.onCompleted(); } } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(); } /** *保存数据 */ private booleanisSave(StringfileName,Tt){ Filefile=newFile(fileDir,fileName); ObjectOutputStreamobjectOut=null; booleanisSuccess=false; try{ FileOutputStreamout=newFileOutputStream(file); objectOut=newObjectOutputStream(out); objectOut.writeObject(t); objectOut.flush(); isSuccess=true; }catch(IOExceptione){ Log.e("写入缓存错误",e.getMessage()); }catch(Exceptione){ Log.e("写入缓存错误",e.getMessage()); }finally{ closeSilently(objectOut); } returnisSuccess; } /** *获取保存的数据 */ privateObjectgetDiskData1(StringfileName){ Filefile=newFile(fileDir,fileName); if(isCacheDataFailure(file)){ returnnull; } if(!file.exists()){ returnnull; } Objecto=null; ObjectInputStreamread=null; try{ read=newObjectInputStream(newFileInputStream(file)); o=read.readObject(); }catch(StreamCorruptedExceptione){ Log.e("读取错误",e.getMessage()); }catch(IOExceptione){ Log.e("读取错误",e.getMessage()); }catch(ClassNotFoundExceptione){ Log.e("错误",e.getMessage()); }finally{ closeSilently(read); } returno; } privatevoidcloseSilently(Closeablecloseable){ if(closeable!=null){ try{ closeable.close(); }catch(Exceptionignored){ } } } /** *判断缓存是否已经失效 */ privatebooleanisCacheDataFailure(FiledataFile){ if(!dataFile.exists()){ returnfalse; } longexistTime=System.currentTimeMillis()-dataFile.lastModified(); booleanfailure=false; if(NetWorkUtil.getNetworkType(CacheLoader.getApplication())==NetWorkUtil.NETTYPE_WIFI){ failure=existTime>WIFI_CACHE_TIME?true:false; }else{ failure=existTime>OTHER_CACHE_TIME?true:false; } returnfailure; } publicvoidclearDisk(Stringkey){ Filefile=newFile(fileDir,key+NAME); if(file.exists())file.delete(); } }
isCacheDataFailure()方式中就是判断当前的数据是否失效,我是根据当前的网络状况来分wifi状况和非wifi状况,wifi状态下数据过期时间比较短,其他状态过期时间比较长。
4.CacheLoader的设计
/ ** *Createdbywukeweion16/6/19. */ publicclassCacheLoader{ privatestaticApplicationapplication; publicstaticApplicationgetApplication(){ returnapplication; } privateICachemMemoryCache,mDiskCache; privateCacheLoader(){ mMemoryCache=newMemoryCache(); mDiskCache=newDiskCache(); } privatestaticCacheLoaderloader; publicstaticCacheLoadergetInstance(Contextcontext){ application=(Application)context.getApplicationContext(); if(loader==null){ synchronized(CacheLoader.class){ if(loader==null){ loader=newCacheLoader(); } } } returnloader; } publicObservable asDataObservable(Stringkey,Class cls,NetworkCache networkCache){ Observableobservable=Observable.concat( memory(key,cls), disk(key,cls), network(key,cls,networkCache)) .first(newFunc1 (){ @Override publicBooleancall(Tt){ returnt!=null; } }); returnobservable; } private Observable memory(Stringkey,Class cls){ returnmMemoryCache.get(key,cls).doOnNext(newAction1 (){ @Override publicvoidcall(Tt){ if(null!=t){ Log.d("我是来自内存","我是来自内存"); } } }); } private Observable disk(finalStringkey,Class cls){ returnmDiskCache.get(key,cls) .doOnNext(newAction1 (){ @Override publicvoidcall(Tt){ if(null!=t){ Log.d("我是来自磁盘","我是来自磁盘"); mMemoryCache.put(key,t); } } }); } private Observable network(finalStringkey,Class cls ,NetworkCache networkCache){ returnnetworkCache.get(key,cls) .doOnNext(newAction1 (){ @Override publicvoidcall(Tt){ if(null!=t){ Log.d("我是来自网络","我是来自网络"); mDiskCache.put(key,t); mMemoryCache.put(key,t); } } }); } publicvoidclearMemory(Stringkey){ ((MemoryCache)mMemoryCache).clearMemory(key); } publicvoidclearMemoryDisk(Stringkey){ ((MemoryCache)mMemoryCache).clearMemory(key); ((DiskCache)mDiskCache).clearDisk(key); } }
5.网络获取的NetworkCache:
/** *Createdbywukeweion16/6/19. */ publicabstractclassNetworkCache{ publicabstractObservable get(Stringkey,finalClass cls); }
6.接下来看怎么使用
/** *Createdbywukeweion16/5/30. */ publicclassItemPresenterextendsBasePresenterimplementsItemContract.Presenter{ privatestaticfinalStringkey="new_list"; protectedintpn=1; protectedvoidreplacePn(){ pn=1; } privatebooleanisRefresh(){ returnpn==1; } privateNetworkCache networkCache; publicItemPresenter(Activityactivity,ItemContract.Viewview){ super(activity,view); } @Override publicvoidgetListData(Stringtype){ if(isRefresh())mView.showLoading(); networkCache=newNetworkCache (){ @Override publicObservable get(Stringkey,Class cls){ returnmHotApi.getPopular(ItemPresenter.this.pn,Constants.PAGE_SIZE,type) .compose(SchedulersCompat.applyIoSchedulers()) .compose(RxResultHelper.handleResult()) .flatMap(populars->{ ListPopularpopular=newListPopular(populars); returnObservable.just(popular); }); } }; Subscriptionsubscription=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都不用给明,要是有好的实现的方式,欢迎告诉我。