在上一篇文章(
http://blog.chinaunix.net/uid-20771867-id-5145681.html)中,我们引入了一个用Rxjava实现的加载图片的框架。但是仅仅是个demo,到底能不能真正地实现三级缓存加载图片呢?下面我们就具体实现这个框架,让其可以完成加载图片的功能吧。
首先重新定义了Data类,在Data类中包含了一个bitmap,用来存储我们要显示的图片,通过url来区分不同的图片,同时添加一个构造方法让其可以从磁盘中加载图片。其中isAvalbile()方法十分重要,Rxjava的first操作符就要根据这个方法的值来判断从哪里来加载图片。
-
public class Data {
-
public Bitmap bitmap;
-
public String url;
-
private boolean isAvailable;
-
-
public Data(Bitmap bitmap, String url) {
-
this.bitmap = bitmap;
-
this.url = url;
-
}
-
-
public Data(File f, String url) {
-
if (f != null && f.exists()) {
-
this.url = url;
-
try {
-
bitmap = BitmapFactory.decodeStream(new FileInputStream(f));
-
} catch (FileNotFoundException e) {
-
e.printStackTrace();
-
}
-
}
-
}
-
-
public boolean isAvailable() {
-
isAvailable = url != null && bitmap != null;
-
return isAvailable;
-
}
-
}
下面来定义一个基类CacheObseravble,所有三级缓存的三个类都继承自这个基类,内部有一个虚函数,需要基类来实现并返回一个Obseravble
-
public abstract class CacheObservable {
-
public abstract Observable<Data> getObservable(String url);
-
}
第一级的缓存是Memory,我们使用LruCache来缓存bitmap对象。
-
public class MemoryCacheOvservable extends CacheObservable {
-
public static final int DEFAULT_CACHE_SIZE = (24 /* MiB */ * 1024 * 1024);
-
MemoryCache<String> mCache = new MemoryCache<>(DEFAULT_CACHE_SIZE);
-
-
@Override
-
public Observable<Data> getObservable(String url) {
-
return Observable.create(subscriber -> {
-
Logger.i("search in memory");
-
if (!subscriber.isUnsubscribed()) {
-
subscriber.onNext(new Data(mCache.get(url), url));
-
subscriber.onCompleted();
-
}
-
});
-
}
-
-
public void putData(Data data) {
-
mCache.put(data.url, data.bitmap);
-
}
-
}
第二级缓存是Disk,这一级里面涉及到了文件的读取和存储操作,所以这些操作都需要放在子线程中来完成,用RxJava来实现简直是小菜一碟。这也是我喜欢RxJava的地方之一,在Android中我们就可以彻底地抛弃AsycTask啦。
-
public class DiskCacheObservable extends CacheObservable {
-
Context mContext;
-
File mCacheFile;
-
-
public DiskCacheObservable(Context mContext) {
-
this.mContext = mContext;
-
mCacheFile = mContext.getCacheDir();
-
}
-
-
@Override
-
public Observable<Data> getObservable(String url) {
-
return Observable.create(new Observable.OnSubscribe<Data>() {
-
@Override
-
public void call(Subscriber<? super Data> subscriber) {
-
Logger.i("read file from disk");
-
File f = getFile(url);
-
Data data = new Data(f, url);
-
subscriber.onNext(data);
-
subscriber.onCompleted();
-
}
-
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
-
}
-
-
private File getFile(String url) {
-
url = url.replaceAll(File.separator, "-");
-
return new File(mCacheFile, url);
-
}
-
-
/**
-
* save pictures downloaded from net to disk
-
* @param data data to be saved
-
*/
-
public void putData(Data data) {
-
Observable.create(new Observable.OnSubscribe<Data>() {
-
@Override
-
public void call(Subscriber<? super Data> subscriber) {
-
File f = getFile(data.url);
-
OutputStream out = null;
-
try {
-
out = new FileOutputStream(f);
-
Bitmap.CompressFormat format;
-
if (data.url.endsWith("png") || data.url.endsWith("PNG")) {
-
format = Bitmap.CompressFormat.PNG;
-
} else {
-
format = Bitmap.CompressFormat.JPEG;
-
}
-
data.bitmap.compress(format, 100, out);
-
out.flush();
-
out.close();
-
} catch (IOException e) {
-
e.printStackTrace();
-
} finally {
-
if (out != null) {
-
try {
-
out.close();
-
} catch (IOException e) {
-
e.printStackTrace();
-
}
-
}
-
}
-
if (!subscriber.isUnsubscribed()) {
-
subscriber.onNext(data);
-
subscriber.onCompleted();
-
}
-
}
-
}).subscribeOn(Schedulers.io()).subscribe();
-
}
-
}
第三级当然是Net了,网络操作也是耗时操作,同样我们要将其放在子线程中进行
-
public class NetCacheObservable extends CacheObservable {
-
@Override
-
public Observable<Data> getObservable(String url) {
-
return Observable.create(new Observable.OnSubscribe<Data>() {
-
@Override
-
public void call(Subscriber<? super Data> subscriber) {
-
Data data;
-
Bitmap bitmap = null;
-
InputStream inputStream = null;
-
Logger.i("get img on net:" + url);
-
try {
-
final URLConnection con = new URL(url).openConnection();
-
inputStream = con.getInputStream();
-
bitmap = BitmapFactory.decodeStream(inputStream);
-
} catch (IOException e) {
-
e.printStackTrace();
-
} finally {
-
if (inputStream != null) {
-
try {
-
inputStream.close();
-
} catch (IOException e) {
-
e.printStackTrace();
-
}
-
}
-
}
-
data = new Data(bitmap, url);
-
if(!subscriber.isUnsubscribed()) {
-
subscriber.onNext(data);
-
subscriber.onCompleted();
-
}
-
}
-
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
-
}
-
}
好了,现在三级缓存的Observable都有了,下一步就是将他们结合起来,按照我们想要的顺序来加载图片。首先我们得能得到这些Observable对象,就是用Source类来统一管理
-
public class Sources {
-
Context mContext;
-
MemoryCacheOvservable mMemoryCacheOvservable;
-
DiskCacheObservable mDiskCacheObservable;
-
NetCacheObservable mNetCacheObservable;
-
-
public Sources(Context mContext) {
-
this.mContext = mContext;
-
mMemoryCacheOvservable = new MemoryCacheOvservable();
-
mDiskCacheObservable = new DiskCacheObservable(mContext);
-
mNetCacheObservable = new NetCacheObservable();
-
}
-
-
-
public Observable<Data> memory(String url) {
-
return mMemoryCacheOvservable.getObservable(url)
-
.compose(logSource("MEMORY"));
-
}
-
-
public Observable<Data> disk(String url) {
-
return mDiskCacheObservable.getObservable(url)
-
.filter(data -> data.bitmap != null)
-
//save picture to disk
-
.doOnNext(mMemoryCacheOvservable::putData)
-
.compose(logSource("DISK"));
-
-
}
-
-
public Observable<Data> network(String url) {
-
return mNetCacheObservable.getObservable(url)
-
.doOnNext(data -> {
-
//save picture to disk and memory
-
mMemoryCacheOvservable.putData(data);
-
mDiskCacheObservable.putData(data);
-
})
-
.compose(logSource("NET"));
-
}
-
-
Observable.Transformer<Data, Data> logSource(final String source) {
-
return dataObservable -> dataObservable.doOnNext(data -> {
-
if (data != null && data.bitmap != null) {
-
Logger.i(source + " has the data you are looking for!");
-
} else {
-
Logger.i(source + " not has the data!");
-
}
-
});
-
}
-
}
终于到了最后一步了,我们无需自己来处理各种情况并决定从何处加载图片,
一切都交给Rxjava来处理,感受Rxjava的强大之处吧。
-
public class RxImageLoader {
-
static Sources sources;
-
-
public static void init(Context mContext) {
-
sources = new Sources(mContext);
-
}
-
-
-
private static final Map<Integer, String> cacheKeysMap = Collections
-
.synchronizedMap(new HashMap<>());
-
-
/**
-
* get the observable that load img and set it to the given ImageView
-
*
-
* @param img the ImageView to show this img
-
* @param url the url for the img
-
* @return the observable to load img
-
*/
-
public static Observable<Data> getLoaderObservable(ImageView img, String url) {
-
if (img != null) {
-
cacheKeysMap.put(img.hashCode(), url);
-
}
-
// Create our sequence for querying best available data
-
Observable<Data> source = Observable.concat(sources.memory(url), sources.disk(url), sources.network(url))
-
.first(data -> data != null && data.isAvailable() && url.equals(data.url));
-
-
return source.doOnNext(data -> {
-
if (img != null && url.equals(cacheKeysMap.get(img.hashCode()))) {
-
img.setImageBitmap(data.bitmap);
-
}
-
});
-
}
-
}
至此,我们的Rxjava实现的三级缓存完全可以使用了,当然还有许多需要继续完善的地方,留待以后慢慢改进吧。看一下运行的效果图:
完整的项目代码见github:
/>
阅读(7134) | 评论(1) | 转发(0) |