网络图片加载以及处理
网络图片加载三级缓存详解以及实现
效果图
1. 讲讲网络图片的加载吧?他们怎么实现的[三级缓存的实现]?
* 网络图片加载流程:判断内存中有没有该图片,如果有的话直接显示,如果内存中没有该图片,则去查看本地[SD卡]是否有该图片的保存,如果本地也没有这张图片,则去网络中加载图片。加载完图片分别保存在本地和缓存中。这样下次再次加载该图片的时候就不用去网络加载。浪费客户流量了。
* 图片三级缓存:
* 内存缓存: 图片加载的时候应该优先加载内存中的图片。速度快。
* 本地缓存: 如果内存中没有该图片,应该优先加载本地中的图片,速度会比内存加载稍慢。
* 网络缓存: 如果内存和本地都没有该图片,网络加载图片,速度取决于网络。
* 三级缓存原理:
* 打开App先去判断缓存和本地有没有该图片,如果缓存和本地都没有,则去网络加载,然后缓存在内存和本地。
* 之后打开App如果内存缓存了该图片则加载内存中的图片,不走本地和网络请求。避免去重复网络请求图片。
2. 网络图片加载代码实现:
* 项目目录:
* 负责判断显示图片 LoadImage.java 类的实现:
public class LoadImage {
private static LoadImage instance;
private DiskCacheUtils mLocalCache;
private MemoryCacheUtils mMemoryCache;
private NetWorkCacheUtils mNetWorkCache;
private LoadImage(Context context) {
mMemoryCache = new MemoryCacheUtils();
mLocalCache = new DiskCacheUtils(context);
mNetWorkCache = new NetWorkCacheUtils(mLocalCache, mMemoryCache);
}
/***
* 单利模式保证LoadImage实例唯一性
*
* @return
*/
public static LoadImage getLoadImageInstance(Context context) {
if (instance == null) {
synchronized (LoadImage.class) {
if (instance == null) {
instance = new LoadImage(context);
}
}
}
return instance;
}
/***
* 显示图片
*
* @param img
* @param imgPath
*/
public void showImg(ImageView img, String imgPath) {
//TODO 先去缓存中读取图片
Bitmap bitmap = null;
bitmap = mMemoryCache.getMemoryBitMap(imgPath);
if (bitmap != null && img.getTag().equals(imgPath)) {
img.setImageBitmap(bitmap);
return;
}
bitmap = mLocalCache.getDiskCacheBitMap(imgPath);
if (bitmap != null && img.getTag().equals(imgPath)) {
img.setImageBitmap(bitmap);
return;
}
mNetWorkCache.getNetWorkBitMap(imgPath,img);
//TODO 加载失败
}
}
- 内存缓存 MemoryCacheUtils .java类代码实现:
public class MemoryCacheUtils {
// private HashMap<String, Bitmap> memoryHashMap = new HashMap<>(); //强引用存储图片 浪费内存
// private HashMap<String, SoftReference<Bitmap>> softReferenceHashMap = new HashMap<>(); //用图片存储采取软引用 系统内存紧张的时候会回收
private LruCache<String, Bitmap> mMemoryLruCache; // Android2.3+后,系统会优先考虑回收弱引用对象,官方提出使用LruCache 所以在用SoftReference不在靠谱
public MemoryCacheUtils() {
int maxMemory = (int) (Runtime.getRuntime().maxMemory()); //最大可用虚拟内存,超过这一个数值将会抛出OutOfMemory / 1024
int cacheSize = maxMemory / 8; //使用第1/8的可用内存为这个内存缓存。
mMemoryLruCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount(); //缓存大小将以千字节为单位而不是项目数。 / 1024
}
};
}
/***
* 读取缓存中的图片
*
* @param imgPath
* @return
*/
public Bitmap getMemoryBitMap(String imgPath) {
// memoryHashMap.get(imgPath);
// SoftReference<Bitmap> softReference = softReferenceHashMap.get(imgPath);
// if (softReference != null) {
// return softReference.get();
// }
Bitmap bitmap = mMemoryLruCache.get(imgPath);
if (bitmap != null) {
return bitmap;
}
return null;
}
/**
* 缓存中存储图片
*
* @param imgPath
* @param bitmap
*/
public void setMemoryBitMap(String imgPath, Bitmap bitmap) {
// memoryHashMap.put(imgPath, bitmap);
// softReferenceHashMap.put(imgPath, new SoftReference(bitmap));
if (getMemoryBitMap(imgPath) == null) {
mMemoryLruCache.put(imgPath, bitmap);
}
}
}
- 本地缓存 DiskCacheUtils.java类实现:
public class DiskCacheUtils {
private final Object mDiskCacheLock = new Object();
private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB
private static final String DISK_CACHE_SUBDIR = "DiskCache";
private Context mContext;
public DiskCacheUtils(Context context) {
}
/***
* 获取内存中的图片
*
* @param imgPath
* @return
*/
public Bitmap getDiskCacheBitMap(String imgPath) {
try {
File file = LoadImageUtils.getDiskDir(mContext, DISK_CACHE_SUBDIR);
if (!file.exists()) {
file.exists();
}
Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(file));
if (bitmap != null) {
return bitmap;
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
Log.e("-->>>路径000","====000" +e.getMessage());
}
return null;
}
/***
* 把图片缓存在本地
*
* @param imgPath
* @param bitMap
*/
public void addBitmapToCache(String imgPath, Bitmap bitMap) {
Log.e("----=====","=======addBitmapToCache");
if (getDiskCacheBitMap(imgPath) != null) {
return;
}
try {
//TODO 应该判断是否有SD卡
File file = LoadImageUtils.getDiskDir(mContext, DISK_CACHE_SUBDIR);
if (!file.exists()) {
file.mkdirs();
}
// File cacheFile = new File(file,imgPath);
bitMap.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(file));
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 网络图片请求,并且缓存之内存中和本地 NetWorkCacheUtils .java:
public class NetWorkCacheUtils {
private DiskCacheUtils mLocaCache;
private MemoryCacheUtils mMemoryCache;
private static final int pixelsWidth = 480, pixelsHeight = 800; //默认手机分辨率设置为 480 * 800 后边可以更改为自动获取
public NetWorkCacheUtils(DiskCacheUtils localCache, MemoryCacheUtils memoryCache) {
this.mLocaCache = localCache;
this.mMemoryCache = memoryCache;
}
public void getNetWorkBitMap(final String imgPath, final ImageView img) {
Log.e("-------", "====" + imgPath);
//下载网络图片 然后返回 为了简单起见直接用OkHttp 下载了 主要是为了写三级缓存 先不管文件下载
OkHttpUtils._downloadAsyn(imgPath, new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//错误了不做任何处理
Log.e("出错了-->", "--->>>>出错啦出错啦");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
InputStream is = response.body().byteStream();
if (is == null) {
return;
}
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; //设置为true 先不把图片读到内存中
BitmapFactory.decodeFile(imgPath, options);
//获取图片原始的宽高
int width = options.outWidth;
int height = options.outHeight;
int inSampleSize = 1;
if (height > pixelsHeight || width > pixelsWidth) {
int heightRatio = Math.round((float) height / (float) pixelsHeight);
int widthRatio = Math.round((float) width / (float) pixelsWidth);
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
options.inSampleSize = inSampleSize;
options.inJustDecodeBounds = false;
options.inSampleSize = 2; //将宽度和高度 压缩成原来的1/2
options.inPreferredConfig = Bitmap.Config.ARGB_4444;
final Bitmap bitmap = BitmapFactory.decodeStream(is, null, options);
if (bitmap != null) {
mLocaCache.addBitmapToCache(imgPath, bitmap);
mMemoryCache.setMemoryBitMap(imgPath, bitmap);
if (img.getTag().equals(imgPath)) { //避免加载图片错位
img.post(new Runnable() {
@Override
public void run() {
img.setImageBitmap(bitmap);
}
});
} else {
img.post(new Runnable() {
@Override
public void run() {
img.setBackgroundColor(Color.parseColor("#FF80AB"));
}
});
}
}
}
}
});
}
}