Android图片的三级缓存和AsyncTask的使用的研究学习

图片的三级缓存

1、优先从内存中加载图片, 速度最快, 不浪费流量

2、其次从本地(sdcard)加载图片, 速度快, 不浪费流量

3、最后从网络下载图片, 速度慢, 浪费流量

XUtils中的BitmapUtils 等开源框架中底层都做了图片的三级缓存。


接下来,我们试着自己写一个类似于 BitmapUtils  图片三级缓存工具类。
MyBitmapUtils.java
/**
 * 自定义三级缓存图片加载工具
 * 
 */
public class MyBitmapUtils {

	private NetCacheUtils mNetCacheUtils;//网络缓存工具类
	private LocalCacheUtils mLocalCacheUtils;//本地缓存工具类
	private MemoryCacheUtils mMemoryCacheUtils;

	public MyBitmapUtils() {
		mMemoryCacheUtils = new MemoryCacheUtils();
		mLocalCacheUtils = new LocalCacheUtils();
		mNetCacheUtils = new NetCacheUtils(mLocalCacheUtils, mMemoryCacheUtils);
	}

	public void display(ImageView imageView, String url) {
		// 设置默认图片
		imageView.setImageResource(R.drawable.pic_item_list_default);

		// 优先从内存中加载图片, 速度最快, 不浪费流量
		Bitmap bitmap = mMemoryCacheUtils.getMemoryCache(url);
		if (bitmap != null) {
			imageView.setImageBitmap(bitmap);
			System.out.println("从内存加载图片啦");
			return;
		}

		// 其次从本地(sdcard)加载图片, 速度快, 不浪费流量
		bitmap = mLocalCacheUtils.getLocalCache(url);
		if (bitmap != null) {
			imageView.setImageBitmap(bitmap);
			System.out.println("从本地加载图片啦");

			// 写内存缓存
			mMemoryCacheUtils.setMemoryCache(url, bitmap);
			return;
		}

		// 最后从网络下载图片, 速度慢, 浪费流量
		mNetCacheUtils.getBitmapFromNet(imageView, url);
	}
}
NetCacheUtils.java网络缓存类
其中用了AsyncTask异步访问网络请求
/**
 * 网络缓存
 * 
 */
public class NetCacheUtils {

	private LocalCacheUtils mLocalCacheUtils;
	private MemoryCacheUtils mMemoryCacheUtils;

	public NetCacheUtils(LocalCacheUtils localCacheUtils,
			MemoryCacheUtils memoryCacheUtils) {
		mLocalCacheUtils = localCacheUtils;
		mMemoryCacheUtils = memoryCacheUtils;
	}

	public void getBitmapFromNet(ImageView imageView, String url) {
		// AsyncTask 异步封装的工具, 可以实现异步请求及主界面更新(对线程池+handler的封装)
		new BitmapTask().execute(imageView, url);// 启动AsyncTask
	}

	/**
	 * 三个泛型意义: 第一个泛型:doInBackground里的参数类型 
	 * 第二个泛型:  onProgressUpdate里的参数类型
	 * 第三个泛型:  onPostExecute里的参数类型及doInBackground的返回类型
	 * 
	 */
	class BitmapTask extends AsyncTask<Object, Integer, Bitmap> {

		private ImageView imageView;
		private String url;

		// 1.预加载, 运行在主线程
		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			// System.out.println("onPreExecute");
		}

		// 2.正在加载, 运行在子线程(核心方法), 可以直接异步请求
		@Override
		protected Bitmap doInBackground(Object... params) {
			// System.out.println("doInBackground");
			imageView = (ImageView) params[0];
			url = (String) params[1];

			imageView.setTag(url);// 打标记, 将当前imageview和url绑定在了一起

			// 开始下载图片
			Bitmap bitmap = download(url);
			// publishProgress(values) 调用此方法实现进度更新(会回调onProgressUpdate)

			return bitmap;
		}

		// 3.更新进度的方法, 运行在主线程
		@Override
		protected void onProgressUpdate(Integer... values) {
			// 更新进度条
			super.onProgressUpdate(values);
		}

		// 4.加载结束, 运行在主线程(核心方法), 可以直接更新UI
		@Override
		protected void onPostExecute(Bitmap result) {
			// System.out.println("onPostExecute");

			if (result != null) {
				// 给imageView设置图片
				// 由于listview的重用机制导致imageview对象可能被多个item共用,
				// 从而可能将错误的图片设置给了imageView对象
				// 所以需要在此处校验, 判断是否是正确的图片
				String url = (String) imageView.getTag();

				if (url.equals(this.url)) {// 判断图片绑定的url是否就是当前bitmap的url					                                                           // 如果是,说明图片正确
					imageView.setImageBitmap(result);
					System.out.println("从网络加载图片啦!!!");

					// 写本地缓存
					mLocalCacheUtils.setLocalCache(url, result);
					// 写内存缓存
					mMemoryCacheUtils.setMemoryCache(url, result);
				}
			}
			super.onPostExecute(result);
		}
	}
	// 下载图片
	public Bitmap download(String url) {
		HttpURLConnection conn = null;
		try {
			conn = (HttpURLConnection) new URL(url).openConnection();

			conn.setRequestMethod("GET");
			conn.setConnectTimeout(5000);// 连接超时
			conn.setReadTimeout(5000);// 读取超时

			conn.connect();

			int responseCode = conn.getResponseCode();

			if (responseCode == 200) {
				InputStream inputStream = conn.getInputStream();

				// 根据输入流生成bitmap对象
				Bitmap bitmap = BitmapFactory.decodeStream(inputStream);

				return bitmap;
			}
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (conn != null) {
				conn.disconnect();
			}
		}

		return null;
	}

}
LocalCacheUtils.java  本地缓存类
/**
 * 本地缓存
 */
public class LocalCacheUtils {

	private static final String LOCAL_CACHE_PATH = Environment
			.getExternalStorageDirectory().getAbsolutePath() + "/image_cache";

	// 写本地缓存
	public void setLocalCache(String url, Bitmap bitmap) {
		File dir = new File(LOCAL_CACHE_PATH);
		if (!dir.exists() || !dir.isDirectory()) {
			dir.mkdirs();// 创建文件夹
		}

		try {
			String fileName = MD5Encoder.encode(url);

			File cacheFile = new File(dir, fileName);

			bitmap.compress(CompressFormat.JPEG, 100, new FileOutputStream(
				cacheFile));// 参数1:图片格式;参数2:压缩比例0-100; 参数3:输出流
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	// 读本地缓存
	public Bitmap getLocalCache(String url) {
		try {
			File cacheFile = new File(LOCAL_CACHE_PATH, MD5Encoder.encode(url));

			if (cacheFile.exists()) {
				Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(
						cacheFile));
				return bitmap;
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
}
MemoryCacheUtils.java  内存缓存类
/**
 * 内存缓存
 */
public class MemoryCacheUtils {
	
	private HashMap<String, Bitmap> mMemoryCache = new HashMap<String,Bitmap>();

	/**
	 * 写缓存
	 */
	public void setMemoryCache(String url, Bitmap bitmap) {	
		mMemoryCache.put(url, bitmap);
	}

	/**
	 * 读缓存
	 */
	public Bitmap getMemoryCache(String url) {

		return mMemoryCache.get(url);
	}
}
但是由于不管android设备总内存是多大, 都只给每个app分配一定内存大小, 16M, 一旦超出16M就内存溢出了。所以内存缓存图片数量过多就会OOM。于是尝试采用软引用SoftReference修改内存缓存类,修改如下:
/**
 * 内存缓存
 */
public class MemoryCacheUtils {
<span style="white-space:pre">	</span>private HashMap<String, SoftReference<Bitmap>> mMemoryCache = new HashMap<String, SoftReference<Bitmap>>();
	/**
	 * 写缓存
	 */
	public void setMemoryCache(String url, Bitmap bitmap) {
		 SoftReference<Bitmap> soft = new SoftReference<Bitmap>(bitmap);//
		// 使用软引用将bitmap包装起来
		 mMemoryCache.put(url, soft);	
	}

	/**
	 * 读缓存
	 */
	public Bitmap getMemoryCache(String url) {
		 SoftReference<Bitmap> softReference = mMemoryCache.get(url);
		
		 if (softReference != null) {
		 Bitmap bitmap = softReference.get();
		 return bitmap;
		 }
		return null;
	}
}
tip:
- 默认强引用, 垃圾回收器不会回收
软引用, 垃圾回收器会考虑回收 SoftReference
弱引用, 垃圾回收器更会考虑回收 WeakReference
虚引用, 垃圾回收器最优先回收 PhantomReference

但是又因为从 Android 2.3 (API Level 9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,这让软引用和弱引用变得不再可靠。Google建议使用LruCache。

下面这个是官方文档的截图:


所以再次修改喽~

修改如下:

/**
 * 内存缓存
 * 因为从 Android 2.3 (API Level 9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,这让 
 * 软引用和弱引用变得不再可靠。Google建议使用LruCache
 */
public class MemoryCacheUtils {

	// private HashMap<String, Bitmap> mMemoryCache = new HashMap<String,
	// Bitmap>();
	// private HashMap<String, SoftReference<Bitmap>> mMemoryCache = new
	// HashMap<String, SoftReference<Bitmap>>();

	private LruCache<String, Bitmap> mMemoryCache;

	public MemoryCacheUtils() {
		// LruCache 可以将最近最少使用的对象回收掉, 从而保证内存不会超出范围
		// Lru: least recentlly used 最近最少使用算法
		long maxMemory = Runtime.getRuntime().maxMemory();// 获取分配给app的内存大小
		System.out.println("maxMemory:" + maxMemory);

		mMemoryCache = new LruCache<String, Bitmap>((int) (maxMemory / 8)) {

			// 返回每个对象的大小
			@Override
			protected int sizeOf(String key, Bitmap value) {
				// int byteCount = value.getByteCount();
				int byteCount = value.getRowBytes() * value.getHeight();// 计算图片大小:每行字节数*高度
				return byteCount;
			}
		};
	}

	/**
	 * 写缓存
	 */
	public void setMemoryCache(String url, Bitmap bitmap) {
		// mMemoryCache.put(url, bitmap);
		// SoftReference<Bitmap> soft = new SoftReference<Bitmap>(bitmap);//
		// 使用软引用将bitmap包装起来
		// mMemoryCache.put(url, soft);
		mMemoryCache.put(url, bitmap);
	}

	/**
	 * 读缓存
	 */
	public Bitmap getMemoryCache(String url) {
		// SoftReference<Bitmap> softReference = mMemoryCache.get(url);
		//
		// if (softReference != null) {
		// Bitmap bitmap = softReference.get();
		// return bitmap;
		// }
		return mMemoryCache.get(url);
	}
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值