我们在开发中用了许多加载图片的框架都封装了缓存机制,图片的三级缓存的核心类就是LruCache类,尝试手写三级缓存工具类:
/**
* 自定义的加载网络图片工具:三级缓存
* @author wangk
*
*/
public class MyBitmapUtils {
private Context context;
private LruCache<String, Bitmap> lruCache;
private File rootFile;//本地缓存根目录
private ExecutorService executorService;
public MyBitmapUtils(Context context) {
this.context = context;
/**
* maxSize:lruCache能管理的最大内存,此处设置为最大内存的1/8
*/
int maxSize = (int) (Runtime.getRuntime().maxMemory()/8);//Runtime.getRuntime().maxMemory()当前应用可使用的最大内存
lruCache = new LruCache<String, Bitmap>(maxSize){
/**
* 当前存储的对象,bitmap,如何计算大小
*/
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getRowBytes() * bitmap.getHeight();//每一行的字节个数*高度 = bitmap的大小
}
};
//本地缓存到data/data
rootFile = context.getFilesDir();
executorService = Executors.newFixedThreadPool(3);//创建固定大小的线程池,内部维护了3个线程
}
/**
* 展示网络图片
* @param image : 展示的空间
* @param imageUrl: 图片的url
*/
public void display(ImageView image, String imageUrl) {
/**
* 三级缓存 (图片)
* 1.内存缓存 HashMap <key,value> key:图片url,value:bitmap
* v4:LruCache 类似hashmap <key,value> lru算法:less recent use,最少最近使用
* 2.本地缓存 保存sd卡,mnt/sdcard/zhbj_10/image/xxx.png
* xxx:图片名称,一般以图片的 url+md5 命名
* url:唯一,还能通过url获取本地缓存图片
* md5加密:为了避免url的特殊字符
* 3.网络缓存
*
* 使用步骤: 遵循原则:优先获取加载速度最快的缓存数据
* 1.从内存中获取,如果获取到图片,直接展示。如果获取不到,从本地缓存获取
* 2.从本地缓存中获取,如果获取到图片,先加载到内存,然后展示。如果获取不到,从网络获取
* 3.从网络获取,如果获取到图片,加载到内存中,然后展示,保存到本地。
*/
//1.从内存中获取图片
Bitmap cacheBitmap = lruCache.get(imageUrl);
if(cacheBitmap!=null){
image.setImageBitmap(cacheBitmap);
System.out.println("从内存中获取");
return ;
}
//2. 从本地获取
String imageName = MD5Encoder.encode(imageUrl);//url+md5加密
File cacheFile = new File(rootFile, imageName);
if(cacheFile.exists() && cacheFile.length()!=0){
//加载到内存中,展示
Bitmap decodeFileBitmap = BitmapFactory.decodeFile(cacheFile.getAbsolutePath());
lruCache.put(imageUrl, decodeFileBitmap);
image.setImageBitmap(decodeFileBitmap);
System.out.println("从本地中获取");
return;
}
//3.从网络获取
int position = (Integer) image.getTag();
executorService.execute(new DownLoadRunnable(image,imageUrl,position));
System.out.println("从网络获取");
}
class DownLoadRunnable implements Runnable{
private ImageView image;
private String imageUrl;
private int position;
/**
*
* @param image
* @param imageUrl
* @param position:请求的位置
*/
public DownLoadRunnable(ImageView image, String imageUrl, int position) {
this.image = image;
this.imageUrl = imageUrl;
this.position = position;
}
@Override
public void run() {
try {
SystemClock.sleep(1000);
//从网络获取:HttpUrlConnection获取网络图片
URL url = new URL(imageUrl);
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
//设置超时时间
httpURLConnection.setConnectTimeout(5*1000);
//设置请求方式
httpURLConnection.setRequestMethod("GET");
//获取响应码
int responseCode = httpURLConnection.getResponseCode();
if(responseCode == 200){//请求成功
InputStream inputStream = httpURLConnection.getInputStream();
final Bitmap decodeStreamBitmap = BitmapFactory.decodeStream(inputStream);
//保存到内存
lruCache.put(imageUrl, decodeStreamBitmap);
//保存到本地(bitmap对象如何转换成本地文件)
/**
* compress:压缩
* format:压缩格式:png、jgp
* quality:压缩质量:0-100,0-完全压缩,100-不压缩
* outputStream:输出流,写到本地
*/
File cacheFile = new File(rootFile, MD5Encoder.encode(imageUrl));
OutputStream ops = new FileOutputStream(cacheFile);
decodeStreamBitmap.compress(CompressFormat.PNG, 100, ops);
//展示
MainActivity mainActivity = (MainActivity) context;
mainActivity.runOnUiThread(new Runnable()//Runnable在子线程还是主线程中运行,主要看调用者
{
@Override
public void run() {
int currentPosition = (Integer) image.getTag();
//请求的位置如果是当前显示在屏幕上的位置
if(position == currentPosition){
image.setImageBitmap(decodeStreamBitmap);
}
}
});
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}