二级缓存
网上很多人说三级缓存,其实都是二级缓存。也就是 内存缓存,文件缓存 。所谓的网络缓存 是不存在的、
下面2级缓存 用RXJAVA 实现。代码没有做优化,但是功能实现了。。。
API_server
public interface GetImage {
@GET("{url}")
Observable<ResponseBody> getimage(@Path("url") String url);
String BASE_URL = "http://img2.baa.bitautotech.com/";
}
RetrofitManager:
public class RetrofitManager {
Retrofit retrofit;
private RetrofitManager() {
retrofit = new Retrofit.Builder()
.baseUrl(GetImage.BASE_URL)
.addCallAdapterFactory(RxJava3CallAdapterFactory.create()) //添加Rxjava
.addConverterFactory(GsonConverterFactory.create()) // <span style="font-family: Arial, Helvetica, sans-serif;">定义转化器 可以将结果返回一个json格式</span>
.build();
}
public static final RetrofitManager Instance = new RetrofitManager();
public static RetrofitManager getInstance() {
return Instance;
}
public GetImage creatserver() {
return retrofit.create(GetImage.class);
}
}
缓存父类```CacheObservable:
public abstract class CacheObservable {
/**
* 获取缓存数据
* @param url
* @return
*/
public Observable<ImageBean> getImage(final String url) {
return Observable.create(new ObservableOnSubscribe<ImageBean>() {
@Override
public void subscribe(@NonNull ObservableEmitter<ImageBean> e) throws Exception {
if (!e.isDisposed()) {
ImageBean image = getDataFromCache(url);
e.onNext(image);
e.onComplete();
}
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
}
/**
* 取出缓存数据
* @param url
* @return
*/
public abstract ImageBean getDataFromCache(String url);
/**
* 缓存数据
* @param image
*/
public abstract void putDataToCache(ImageBean image);
}
内存子类:MemoryCacheObservable
public class MemoryCacheObservable extends CacheObservable {
private int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
private int cacheSize = maxMemory / 4;
private LruCache<String, Bitmap> mLruCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
}
};
@Override
public ImageBean getDataFromCache(String url) {
Log.e("getDataFromCache", "getDataFromMemoryCache");
Bitmap bitmap = mLruCache.get(url);
return new ImageBean(bitmap, url);
}
@Override
public void putDataToCache(ImageBean image) {
mLruCache.put(image.getUrl(), image.getBitmap());
Log.e("putDataToCache", "putDataToCache");
}
}
文件子类:DiskCacheObservable,文件缓存子类用的是 OKHTT3 的DiskLruCache
public class DiskCacheObservable extends CacheObservable {
private Context mContext;
private DiskLruCache mDiskLruCache;
private final int maxSize = 10 * 1024 * 1024;
public DiskCacheObservable(Context mContext) {
this.mContext = mContext;
initDiskLruCache();
}
@Override
public ImageBean getDataFromCache(String url) {
Bitmap bitmap = getDataFromDiskLruCache(url);
Log.e("getDataFromCache", "bitmap.toString()");
return new ImageBean(bitmap, url);
}
@Override
public void putDataToCache(ImageBean imageBean) {
RetrofitManager.getInstance().creatserver().getimage(imageBean.getUrl()).doOnNext(new Consumer<ResponseBody>() {
@Override
public void accept(ResponseBody responseBody) throws Throwable {
String key = DiskCacheUtils.getMD5String(imageBean.getUrl());
DiskLruCache.Editor editor = null;
try {
editor = mDiskLruCache.edit(key);
} catch (IOException e) {
e.printStackTrace();
}
Sink sink = editor.newSink(0);
Log.e("sink", "sink");
InputStream inputStream = null;
try {
//long fileSize = body.contentLength();
//long fileSizeDownloaded = 0;
byte[] fileReader = new byte[4096];
Buffer buffer = new Buffer();
inputStream = responseBody.byteStream();
while (true) {
int read = inputStream.read(fileReader);
if (read == -1) {
break;
}
buffer.write(fileReader, 0, read);
//fileSizeDownloaded += read;
sink.write(buffer, read);
buffer.clear();
}
if (buffer != null) {
buffer.clear();
buffer.close();
}
if (inputStream != null) {
inputStream.close();
}
if (sink != null) {
sink.flush();
sink.close();
}
editor.commit();
} catch (IOException e) {
e.printStackTrace();
}
}
}).subscribe();
}
public void initDiskLruCache() {
File cacheDir = DiskCacheUtils.getDiskCacheDir(mContext, "our22_cache");
if (!cacheDir.exists()) {
cacheDir.mkdirs();
Log.e("cach---eDir", cacheDir.getPath());
}
int versionCode = DiskCacheUtils.getAppVersion(mContext);
//这里需要注意参数二:缓存版本号,只要不同版本号,缓存都会被清除,重新使用新的
mDiskLruCache = DiskLruCache.create(FileSystem.SYSTEM, cacheDir, versionCode, 1, maxSize);
}
/**
* 获取文件缓存
*
* @param url
* @return
*/
private Bitmap getDataFromDiskLruCache(String url) {
Bitmap bitmap = null;
try {
final String key = DiskCacheUtils.getMD5String(url);
DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
if (snapshot == null) return null;
//获取资源的输出流,Source类似InputStream
Source source = snapshot.getSource(0);
Buffer buffer = new Buffer();
//读取4*1024数据放入buffer中并返回读取到的数据的字节长度
long ret = source.read(buffer, 4 * 1024);
//判断文件是否读完
while (ret != -1) {
ret = source.read(buffer, 4 * 1024);
}
//获取到buffer的inputStream对象
InputStream inputStream = buffer.inputStream();
bitmap = BitmapFactory.decodeStream(inputStream);
if (inputStream != null) {
inputStream.close();
}
if (buffer != null) {
buffer.clear();
buffer.close();
}
if (source != null) {
source.close();
}
if (snapshot != null) {
snapshot.close();
}
return bitmap;
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
}
文件帮助类:网上抄袭来的,拿来主义,呵呵。DiskCacheUtils
public class DiskCacheUtils {
/**
* 获取缓存路径
* @param context
* @param uniqueName
* @return
*/
public static File getDiskCacheDir(Context context, String uniqueName) {
String cachePath;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
|| !Environment.isExternalStorageRemovable()) {
cachePath = context.getExternalCacheDir().getPath();
} else {
cachePath = context.getCacheDir().getPath();
}
Log.e("cachePath",cachePath);
return new File(cachePath + File.separator + uniqueName);
}
/**
* 获取App版本号
* @param context
* @return
*/
public static int getAppVersion(Context context) {
try {
PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
return info.versionCode;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return 1;
}
/**
* 获取加密后的MD5
* @param key
* @return
*/
public static String getMD5String(String key) {
String cacheKey;
try {
final MessageDigest mDigest = MessageDigest.getInstance("MD5");
mDigest.update(key.getBytes());
cacheKey = bytesToHexString(mDigest.digest());
} catch (NoSuchAlgorithmException e) {
cacheKey = String.valueOf(key.hashCode());
}
return cacheKey;
}
private static String bytesToHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(0xFF & bytes[i]);
if (hex.length() == 1) {
sb.append('0');
}
sb.append(hex);
}
return sb.toString();
}
}
ImageBean 实体。
{ private String url;
private Bitmap bitmap;}
RequestCreator 构建者模式,链式调用。
public class RequestCreator {
public MemoryCacheObservable memoryCacheObservable;
public DiskCacheObservable diskCacheObservable;
public RequestCreator(Context context) {
memoryCacheObservable = new MemoryCacheObservable();
diskCacheObservable = new DiskCacheObservable(context);
}
public Observable<ImageBean> getImageFromMemory(String url) {
return memoryCacheObservable.getImage(url)
.filter(new Predicate<ImageBean>() {
@Override
public boolean test(@NonNull ImageBean imageBean) throws Exception {
Bitmap bitmap = imageBean.getBitmap();
Log.e("getImageFromMemory", "null");
return bitmap != null;
}
});
}
public Observable<ImageBean> getImageFromDisk(String url) {
return diskCacheObservable.getImage(url)
.filter(new Predicate<ImageBean>() {
@Override
public boolean test(@NonNull ImageBean imageBean) throws Exception {
Bitmap bitmap = imageBean.getBitmap();
if (bitmap != null) {
return true;
} else {
diskCacheObservable.putDataToCache(imageBean);
ImageBean imageBean1 = diskCacheObservable.getDataFromCache(imageBean.getUrl());
if (imageBean1.getBitmap() != null) {
Log.e("diskCacheObservable.putDataToCache", "true");
return true;
}
}
return true;
}
}).doOnNext(new Consumer<ImageBean>() {
@Override
public void accept(@NonNull ImageBean imageBean) throws Exception {
//缓存内存
Log.e("memoryCacheObservable.putDataToCache", "缓存到内存");
memoryCacheObservable.putDataToCache(imageBean);
}
});
}
}
RxImageLoader 类
public class RxImageLoader {
static RxImageLoader singleton;
private String mUrl;
private RequestCreator requestCreator;
//防止用户可以创建该对象
private RxImageLoader(Builder builder) {
requestCreator = new RequestCreator(builder.mContext);
}
public static RxImageLoader with(Context context) {
if (singleton == null) {
synchronized (RxImageLoader.class) {
if (singleton == null) {
singleton = new Builder(context).build();
}
}
}
return singleton;
}
public RxImageLoader load(String url) {
this.mUrl = url;
return singleton;
}
public void into(final ImageView imageView) {
Observable
.concat(
requestCreator.getImageFromMemory(mUrl),
requestCreator.getImageFromDisk(mUrl)
// requestCreator.getImageFromNetwork(mUrl)
)
.first(new ImageBean(null, mUrl)).toObservable()
.retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
private int retryCount;
@Override
public ObservableSource<?> apply(Observable<Throwable> throwableObservable) throws Throwable {
return throwableObservable
.flatMap((Throwable throwable) -> {
if (++retryCount <= 3) {
// When this Observable calls onNext, the original Observable will be retried (i.e. re-subscribed).
Log.e("get error, retry count " ,retryCount+"-");
return Observable.just("" + retryCount).delay(5,TimeUnit.SECONDS);
}
// Max retries hit. Just pass the error along.
return Observable.error(throwable);
});
}
})
.subscribe(new Observer<ImageBean>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(ImageBean imageBean) {
Log.e("onNext", imageBean.getBitmap() + "--" + imageBean.getUrl());
if (imageBean.getBitmap() != null) {
imageView.setImageBitmap(imageBean.getBitmap());
}
}
@Override
public void onError(Throwable e) {
Log.e("eeeeeeeee", e.getMessage());
e.printStackTrace();
}
@Override
public void onComplete() {
Log.e("onComplete", "onComplete");
}
});
//
// Observable.concat(requestCreator.getImageFromMemory(mUrl),
// requestCreator.getImageFromDisk(mUrl),
// requestCreator.getImageFromNetwork(mUrl)
// )
//
// .subscribeOn(Schedulers.io())
// .firstElement()
// .observeOn(AndroidSchedulers.mainThread())
// .subscribe(new Consumer<ImageBean>() {
// @Override
// public void accept(ImageBean imageBean) throws Throwable {
// Log.e("imageBean", imageBean.getUrl());
// }
// }, new Consumer<Throwable>() {
// @Override
// public void accept(Throwable throwable) throws Throwable {
// Log.e("throwable", throwable.getMessage());
// }
// });
}
public static class Builder {
private Context mContext;
public Builder(Context mContext) {
this.mContext = mContext;
}
public RxImageLoader build() {
return new RxImageLoader(this);
}
}
}
在Test2Activity中调用就简单了,一句话解决:
RxImageLoader.with(Test2Activity.this).load(url).into(iv);