Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1571823
  • 博文数量: 113
  • 博客积分: 3526
  • 博客等级: 中校
  • 技术积分: 1815
  • 用 户 组: 普通用户
  • 注册时间: 2009-09-08 09:46
个人简介

记录总结自己的工作

文章分类

全部博文(113)

文章存档

2015年(19)

2014年(10)

2013年(6)

2012年(16)

2011年(24)

2010年(21)

2009年(17)

分类: Android平台

2015-09-02 14:51:10

    在上一篇文章(http://blog.chinaunix.net/uid-20771867-id-5145681.html)中,我们引入了一个用Rxjava实现的加载图片的框架。但是仅仅是个demo,到底能不能真正地实现三级缓存加载图片呢?下面我们就具体实现这个框架,让其可以完成加载图片的功能吧。

    首先重新定义了Data类,在Data类中包含了一个bitmap,用来存储我们要显示的图片,通过url来区分不同的图片,同时添加一个构造方法让其可以从磁盘中加载图片。其中isAvalbile()方法十分重要,Rxjava的first操作符就要根据这个方法的值来判断从哪里来加载图片。

点击(此处)折叠或打开

  1. public class Data {
  2.     public Bitmap bitmap;
  3.     public String url;
  4.     private boolean isAvailable;

  5.     public Data(Bitmap bitmap, String url) {
  6.         this.bitmap = bitmap;
  7.         this.url = url;
  8.     }

  9.     public Data(File f, String url) {
  10.         if (f != null && f.exists()) {
  11.             this.url = url;
  12.             try {
  13.                 bitmap = BitmapFactory.decodeStream(new FileInputStream(f));
  14.             } catch (FileNotFoundException e) {
  15.                 e.printStackTrace();
  16.             }
  17.         }
  18.     }

  19.     public boolean isAvailable() {
  20.         isAvailable = url != null && bitmap != null;
  21.         return isAvailable;
  22.     }
  23. }

    下面来定义一个基类CacheObseravble,所有三级缓存的三个类都继承自这个基类,内部有一个虚函数,需要基类来实现并返回一个Obseravble

点击(此处)折叠或打开

  1. public abstract class CacheObservable {
  2.     public abstract Observable<Data> getObservable(String url);
  3. }

    第一级的缓存是Memory,我们使用LruCache来缓存bitmap对象。

点击(此处)折叠或打开

  1. public class MemoryCacheOvservable extends CacheObservable {
  2.     public static final int DEFAULT_CACHE_SIZE = (24 /* MiB */ * 1024 * 1024);
  3.     MemoryCache<String> mCache = new MemoryCache<>(DEFAULT_CACHE_SIZE);

  4.     @Override
  5.     public Observable<Data> getObservable(String url) {
  6.         return Observable.create(subscriber -> {
  7.             Logger.i("search in memory");
  8.             if (!subscriber.isUnsubscribed()) {
  9.                 subscriber.onNext(new Data(mCache.get(url), url));
  10.                 subscriber.onCompleted();
  11.             }
  12.         });
  13.     }

  14.     public void putData(Data data) {
  15.         mCache.put(data.url, data.bitmap);
  16.     }
  17. }
    第二级缓存是Disk,这一级里面涉及到了文件的读取和存储操作,所以这些操作都需要放在子线程中来完成,用RxJava来实现简直是小菜一碟。这也是我喜欢RxJava的地方之一,在Android中我们就可以彻底地抛弃AsycTask啦。

点击(此处)折叠或打开

  1. public class DiskCacheObservable extends CacheObservable {
  2.     Context mContext;
  3.     File mCacheFile;

  4.     public DiskCacheObservable(Context mContext) {
  5.         this.mContext = mContext;
  6.         mCacheFile = mContext.getCacheDir();
  7.     }

  8.     @Override
  9.     public Observable<Data> getObservable(String url) {
  10.         return Observable.create(new Observable.OnSubscribe<Data>() {
  11.             @Override
  12.             public void call(Subscriber<? super Data> subscriber) {
  13.                 Logger.i("read file from disk");
  14.                 File f = getFile(url);
  15.                 Data data = new Data(f, url);
  16.                 subscriber.onNext(data);
  17.                 subscriber.onCompleted();
  18.             }
  19.         }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
  20.     }

  21.     private File getFile(String url) {
  22.         url = url.replaceAll(File.separator, "-");
  23.         return new File(mCacheFile, url);
  24.     }

  25.     /**
  26.      * save pictures downloaded from net to disk
  27.      * @param data data to be saved
  28.      */
  29.     public void putData(Data data) {
  30.         Observable.create(new Observable.OnSubscribe<Data>() {
  31.             @Override
  32.             public void call(Subscriber<? super Data> subscriber) {
  33.                 File f = getFile(data.url);
  34.                 OutputStream out = null;
  35.                 try {
  36.                     out = new FileOutputStream(f);
  37.                     Bitmap.CompressFormat format;
  38.                     if (data.url.endsWith("png") || data.url.endsWith("PNG")) {
  39.                         format = Bitmap.CompressFormat.PNG;
  40.                     } else {
  41.                         format = Bitmap.CompressFormat.JPEG;
  42.                     }
  43.                     data.bitmap.compress(format, 100, out);
  44.                     out.flush();
  45.                     out.close();
  46.                 } catch (IOException e) {
  47.                     e.printStackTrace();
  48.                 } finally {
  49.                     if (out != null) {
  50.                         try {
  51.                             out.close();
  52.                         } catch (IOException e) {
  53.                             e.printStackTrace();
  54.                         }
  55.                     }
  56.                 }
  57.                 if (!subscriber.isUnsubscribed()) {
  58.                     subscriber.onNext(data);
  59.                     subscriber.onCompleted();
  60.                 }
  61.             }
  62.         }).subscribeOn(Schedulers.io()).subscribe();
  63.     }
  64. }
     
    第三级当然是Net了,网络操作也是耗时操作,同样我们要将其放在子线程中进行

点击(此处)折叠或打开

  1. public class NetCacheObservable extends CacheObservable {
  2.     @Override
  3.     public Observable<Data> getObservable(String url) {
  4.         return Observable.create(new Observable.OnSubscribe<Data>() {
  5.             @Override
  6.             public void call(Subscriber<? super Data> subscriber) {
  7.                 Data data;
  8.                 Bitmap bitmap = null;
  9.                 InputStream inputStream = null;
  10.                 Logger.i("get img on net:" + url);
  11.                 try {
  12.                     final URLConnection con = new URL(url).openConnection();
  13.                     inputStream = con.getInputStream();
  14.                     bitmap = BitmapFactory.decodeStream(inputStream);
  15.                 } catch (IOException e) {
  16.                     e.printStackTrace();
  17.                 } finally {
  18.                     if (inputStream != null) {
  19.                         try {
  20.                             inputStream.close();
  21.                         } catch (IOException e) {
  22.                             e.printStackTrace();
  23.                         }
  24.                     }
  25.                 }
  26.                 data = new Data(bitmap, url);
  27.                 if(!subscriber.isUnsubscribed()) {
  28.                     subscriber.onNext(data);
  29.                     subscriber.onCompleted();
  30.                 }
  31.             }
  32.         }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
  33.     }
  34. }

    好了,现在三级缓存的Observable都有了,下一步就是将他们结合起来,按照我们想要的顺序来加载图片。首先我们得能得到这些Observable对象,就是用Source类来统一管理

点击(此处)折叠或打开

  1. public class Sources {
  2.     Context mContext;
  3.     MemoryCacheOvservable mMemoryCacheOvservable;
  4.     DiskCacheObservable mDiskCacheObservable;
  5.     NetCacheObservable mNetCacheObservable;

  6.     public Sources(Context mContext) {
  7.         this.mContext = mContext;
  8.         mMemoryCacheOvservable = new MemoryCacheOvservable();
  9.         mDiskCacheObservable = new DiskCacheObservable(mContext);
  10.         mNetCacheObservable = new NetCacheObservable();
  11.     }


  12.     public Observable<Data> memory(String url) {
  13.         return mMemoryCacheOvservable.getObservable(url)
  14.                 .compose(logSource("MEMORY"));
  15.     }

  16.     public Observable<Data> disk(String url) {
  17.         return mDiskCacheObservable.getObservable(url)
  18.                 .filter(data -> data.bitmap != null)
  19.                 //save picture to disk
  20.                 .doOnNext(mMemoryCacheOvservable::putData)
  21.                 .compose(logSource("DISK"));

  22.     }

  23.     public Observable<Data> network(String url) {
  24.         return mNetCacheObservable.getObservable(url)
  25.                 .doOnNext(data -> {
  26.                     //save picture to disk and memory
  27.                     mMemoryCacheOvservable.putData(data);
  28.                     mDiskCacheObservable.putData(data);
  29.                 })
  30.                 .compose(logSource("NET"));
  31.     }

  32.     Observable.Transformer<Data, Data> logSource(final String source) {
  33.         return dataObservable -> dataObservable.doOnNext(data -> {
  34.             if (data != null && data.bitmap != null) {
  35.                 Logger.i(source + " has the data you are looking for!");
  36.             } else {
  37.                 Logger.i(source + " not has the data!");
  38.             }
  39.         });
  40.     }
  41. }

    终于到了最后一步了,我们无需自己来处理各种情况并决定从何处加载图片,一切都交给Rxjava来处理,感受Rxjava的强大之处吧。

点击(此处)折叠或打开

  1. public class RxImageLoader {
  2.     static Sources sources;

  3.     public static void init(Context mContext) {
  4.         sources = new Sources(mContext);
  5.     }


  6.     private static final Map<Integer, String> cacheKeysMap = Collections
  7.             .synchronizedMap(new HashMap<>());

  8.     /**
  9.      * get the observable that load img and set it to the given ImageView
  10.      *
  11.      * @param img the ImageView to show this img
  12.      * @param url the url for the img
  13.      * @return the observable to load img
  14.      */
  15.     public static Observable<Data> getLoaderObservable(ImageView img, String url) {
  16.         if (img != null) {
  17.             cacheKeysMap.put(img.hashCode(), url);
  18.         }
  19.         // Create our sequence for querying best available data
  20.         Observable<Data> source = Observable.concat(sources.memory(url), sources.disk(url), sources.network(url))
  21.                 .first(data -> data != null && data.isAvailable() && url.equals(data.url));

  22.         return source.doOnNext(data -> {
  23.             if (img != null && url.equals(cacheKeysMap.get(img.hashCode()))) {
  24.                 img.setImageBitmap(data.bitmap);
  25.             }
  26.         });
  27.     }
  28. }
    至此,我们的Rxjava实现的三级缓存完全可以使用了,当然还有许多需要继续完善的地方,留待以后慢慢改进吧。看一下运行的效果图:


     完整的项目代码见github:  />


阅读(7161) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

muximus32015-11-05 14:51:13

文明上网,理性发言...