在我们的app中,有些图片可能会反复使用到,如果每次使用都从网络加载的话既浪费流量又浪费资源,so图片的三级缓存必须实现,实现的步骤思路如下:
- 缓存在内存中使用LruCashe是最好的了,不需要什么弱引用强引用,LruCache满足先进先出的原则,当我们为图片缓存分配的内存不够时他会将使用最少的或者最先存入的图片删除
- 缓存到磁盘中使用原生的IO即可
- 从网络加载使用IO(可根据实际情况选择)
新建ImageHelper类如下,
public class ImageHelper {
//内存缓存池
private static LruCache<String,Bitmap> mCache;
/开辟子线程执行任务
private static Handler mHandler;
//线程管理者
private static ExecutorService mThreadPoor;
private static Map<ImageView,Future<?>> mTaskTags = new LinkedHashMap ImageView,Future<?>>();
private Context mContext;
public ImageHelper(Context context){
this.mContext = context;
if(mCache==null){
//最大使用的内存空间 为剩余内存的四分之一
int maxSize = (int)(Runtime.getRuntime().freeMemory()/4);
mCache = new LruCache<String, Bitmap>(maxSize){
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes()*value.getHeight();
}
};
if(mHandler==null){
mHandler = new Handler() ;
}
if(mThreadPoor==null){
//最多同时允许的线程数为三个
mThreadPoor = Executors.newFixedThreadPool(3);
}
}
}
public void display(ImageView iv,String url){
//1.去内存中取
Bitmap bitmap = mCache.get(url);
if(bitmap!=null){
iv.setImageBitmap(bitmap);
return;
}
//2.去硬盘上取
try {
bitmap = loadBitmapFromLocal(url);
} catch (Exception e) {
e.printStackTrace();
}
if(bitmap!=null){
iv.setImageBitmap(bitmap);
return;
}
//3.从网络获取图片
loadBitMapFromNet(iv,url);
}
private void loadBitMapFromNet(ImageView iv,String url){
Future<?> future = mTaskTags.get(iv);
if(future!=null&&!future.isCancelled()&&!future.isDone()){
Log.i("正在加载","取消当前任务");
future.cancel(true);
future=null;
}
future = mThreadPoor.submit( new ImageLoadTask(iv,url));
mTaskTags.put(iv,future);
Log.i("标记任务","");
}
class ImageLoadTask implements Runnable{
private ImageView iv;
private String url;
public ImageLoadTask(ImageView iv,String url){
this.iv = iv;
this.url = url;
}
@Override
public void run() {
try {
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setConnectTimeout(3*1000);
conn.setReadTimeout(30*1000);
conn.connect();
int code = conn.getResponseCode();
if(code==200){
InputStream is = conn.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(is);
// 存入内存池
mCache.put(url,bitmap);
try {
//写入硬盘
writeToLocal(url,bitmap );
} catch (Exception e) {
e.printStackTrace();
}
mHandler.post(new Runnable() {
@Override
public void run() {
//加载图片
display(iv,url);
}
});
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//从硬盘中获取图片
private Bitmap loadBitmapFromLocal(String url) throws Exception {
String name;
name = MD5Encoder.encode(url);
File file = new File(getCacheDir(),name);
if(file.exists()){
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
mCache.put(url,bitmap);
return bitmap;
}
return null;
}
private void writeToLocal(String url,Bitmap bitmap) throws Exception {
String name;
FileOutputStream fos = null;
name = MD5Encoder.encode(url);
File file = new File(getCacheDir(),name);
fos = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG,100,fos);
if(fos!=null){
fos.close();
fos=null;
}
}
private String getCacheDir(){
String state = Environment.getExternalStorageState();
File dir = null;
if(Environment.MEDIA_MOUNTED.equals(state)){
//有sd卡
dir = new
File(Environment.getExternalStorageDirectory(),"/Android/data/"+mContext.getPackageName()+"/Icon");
}else{
dir = new File(mContext.getCacheDir(),"/Icon");
}
if(!dir.exists()){
dir.mkdirs(); }
return dir.getAbsolutePath(); }}
附带 MD5Encoder帮助类:
public class MD5Encoder {
public static String encode(String string) throws Exception {
byte[] hash = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8"));
StringBuilder hex = new StringBuilder(hash.length * 2);
for (byte b : hash) {
if ((b & 0xFF) < 0x10) {
hex.append("0");
}
hex.append(Integer.toHexString(b & 0xFF));
}
return hex.toString();
}
}