标签: Android图片缓存
2013-04-11 10:01
3438人阅读
收藏
举报
- "font-family: 'Microsoft YaHei';">1.意义:加快读取速度,减少流量的消耗,减少崩溃的次数
2.Android应用中的UI现成5秒没有相应的话就会强制抛出异常,俗称ANR(Appliction Not Responce),对于获取远程的资源,这里特指的是从服务器获取的数据譬如图片等等,这种异常将会更加容易被抛出来,所以在android 4.0 里面将限制了网络的访问,不允许将网络的访问放在主线程,低于4.0的版本就不会收到限制,这个是在测试的时候发现的,Android中提供两个方法来做这件事情:
启动一个心的现成来获取资源,完成后通过handler机制发送消息,同时在handler的接收端更新主线程,从而达到异步线程获取图片,接收端定义handler变量,同事复写handlMessage(Message msg)方法
本地缓存
对图片来说,你不可能让应用每次获取的时候都重新到远程服务器去下载,特别是显示ListView中的图片的时候,滑动的速度变得很快,这样将会造成ANR,即使图片比较小,但是图片还没来得及释放的话,累计的图片将会占用比较大的内存,但是又不能将所有的图片资源在获取之后放在内存中,使用弱引用保存对象的方法保存,因为图片的资源往往很占内存也比较容易造成ANR,那么如果下载下来的图片保存的SdCard中,下次直接从SDcard上去获取的话,是比较靠谱的缓存方法,采用LRU等一些算法可以保证sdcard被占用的空间的一小部分,这样即保证了图片的加载,节省了从远程获取的图片流量,又使Sdcard的空间只占用了一笑部分,另外一中方法是设置LRU规则跟过期的时间
代码的流程如下:
下载图片--->判断Sdcard上的空间--->判断开辟的10Mde空间--->保存图片--->过期策略
2.1在Sdcard上开辟一定的空间,需要先判断Sdcard上剩余的空间是否足够,如果足够的话,就可以开辟空间,例如开辟10M的内存空间用来保存图片
2.2当需要获取图片的时候,就先从Sdcard上的目录中去找,如果找的到的话,使用该图片,并且更新图片最后被使用的时间,如果找不到,通过URL去DownLoad
2.3去服务器下载图片,如果图片下载成功了,放入SDcard,并使用,如果失败了,应该有重试的机制重新下载,譬如三次
2.4下载成功后保存到Sdcard上需要判断10M的空间是否已经用完,如果没用完就保存,如果已经用完空间,就根据LRU规则删除一些最近没有被用户用到的资源
保存图片到SD的代码:
- private void saveBmpToSd(Bitmap bm, String url) {
- if (bm == null) {
- Log.w(TAG, " trying to savenull bitmap");
- return;
- }
-
- if (FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
- Log.w(TAG, "Low free space onsd, do not cache");
- return;
- }
- String filename = convertUrlToFileName(url);
- String dir = getDirectory(filename);
- File file = new File(dir + "/" + filename);
- try {
- file.createNewFile();
- OutputStream outStream = new FileOutputStream(file);
- bm.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
- outStream.flush();
- outStream.close();
- Log.i(TAG, "Image saved tosd");
- } catch (FileNotFoundException e) {
- Log.w(TAG, "FileNotFoundException");
- } catch (IOException e) {
- Log.w(TAG, "IOException");
- }
- }
计算Sdcard上的空间:
-
-
-
-
-
- private int freeSpaceOnSd() {
-
- StatFs stat = new StatFs(Environment.getExternalStorageDirectory()
- .getPath());
- double sdFreeMB = ((double) stat.getAvailableBlocks() * (double) stat
- .getBlockSize());
- return (int) sdFreeMB;
- }
修改文件的最后修改时间:
-
-
-
-
-
-
- private void updateFileTime(String dir, String fileName) {
- File file = new File(dir, fileName);
- long newModifiedTime = System.currentTimeMillis();
- file.setLastModified(newModifiedTime);
- }
本地缓存优化:
-
-
-
-
-
-
- private void removeCache(String dirPath) {
- File file = new File(dirPath);
- File[] files = file.listFiles();
- if (files == null) {
- return;
- }
- int dirSize = 0;
- for (int i = 0; i < files.length; i++) {
- if (files[i].getName().contains(WHOLESALE_CONV)) {
- dirSize += files[i].length();
- }
- }
- if (dirSize > CACHE_SIZE * MB
- || FREE_SD_SPACE_NEEED_TO_CACHE > freeSpaceOnSd()) {
- int removeFactor = (int) ((0.4 * files.length) + 10);
- Arrays.sort(files, new FileLastModifSort());
- Log.i(TAG, "Clear some expiredcache files");
- for (int i = 0; i < removeFactor; i++) {
- if (files[i].getName().contains(WHOLESALE_CONV)) {
- files[i].delete();
- }
- }
- }
- }
-
-
-
-
-
- private void removeExpiredCache(String dirPath, String fileName) {
- File file = new File(dirPath, fileName);
- if (System.currentTimeMillis() - file.lastModified() > M_TIME_DIFF) {
- Log.i(TAG, "Clear some expiredcache files");
- file.delete();
- }
- }
文件使用时间排序:
-
-
-
-
-
- class FileLastModifSort implements Comparator {
- public int compare(File arg0, File arg1) {
- if (arg0.lastModified() > arg1.lastModified()) {
- return 1;
- } else if (arg0.lastModified() == arg1.lastModified()) {
- return 0;
- } else {
- return -1;
- }
- }
- }
内存保存:
在内存中保存的话,只能保存一定的量,而不能一直往里面放,需要设置数据的过期时间,LRU等算法,这里有一个方法是把常用的数据放到一个缓存中(A),不常用的放在另外一个缓存中(B),当要获取数据时候先从A中去获取,如果A中存在在去B中获取,B中的数据主要是A中LUR出来的数据,这里的内存回收主要是针对B,从而保持A中的数据可以有效的被命中。
定义A缓存:
-
- private final HashMap mHardBItmapCache = new LinkedHashMap(
- HARD_CACHE_CAPACITY / 2, 0.75f, true) {
- @Override
- protected boolean removeEldestEntry(
- java.util.Map.Entry eldest) {
-
- if (size() > HARD_CACHE_CAPACITY) {
-
- mSoftBitmapCache.put(eldest.getKey(),
- new SoftReference(eldest.getValue()));
- return true;
- } else {
- return false;
- }
- }
- };
定义B缓存:
-
-
-
- private final static ConcurrentHashMap> mSoftBitmapCache = new ConcurrentHashMap>(
- HARD_CACHE_CAPACITY / 2);
从缓存中获取数据:
-
-
-
-
-
-
- private Bitmap getBitmapFromeCache(String url) {
-
- synchronized (mHardBItmapCache) {
- final Bitmap bitmap = mHardBItmapCache.get(url);
- if (bitmap != null) {
-
- mHardBItmapCache.remove(url);
- mHardBItmapCache.put(url, bitmap);
- return bitmap;
- }
- }
-
- SoftReference bitmapReference = mSoftBitmapCache.get(url);
- if (bitmapReference != null) {
- final Bitmap bitmap = bitmapReference.get();
- if (bitmap != null) {
- return bitmap;
- } else {
- mSoftBitmapCache.remove(url);
- }
- }
- return null;
- }
如果缓存不存在,那么就只能去服务器下载:
-
- class ImageDownloaderTask extends AsyncTask {
- private static final int IO_BUFFER_SIZE = 4 * 1024;
- private String url;
- private final WeakReference imageViewReferrReference;
-
- private ImageDownloaderTask() {
- imageViewReferrReference = new WeakReference(imageView);
-
- }
-
- @Override
- protected Bitmap doInBackground(String... params) {
-
- final AndroidHttpClient client = AndroidHttpClient
- .newInstance("Android");
- url = params[0];
- final HttpGet getRequest = new HttpGet(url);
- try {
- HttpResponse response = client.execute(getRequest);
- final int statusCode = response.getStatusLine().getStatusCode();
- if (statusCode != HttpStatus.SC_OK) {
- Log.v(TAG, "从" + url + "中下载图片是出错! 错误码:" + statusCode);
- return null;
- }
- final HttpEntity entity = response.getEntity();
- if (entity != null) {
- InputStream inputStream = null;
- OutputStream outputStream = null;
- try {
- inputStream = entity.getContent();
- final ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
- outputStream = new BufferedOutputStream(dataStream,
- IO_BUFFER_SIZE);
- copy(inputStream, outputStream);
- outputStream.flush();
- final byte[] data = dataStream.toByteArray();
- final Bitmap bitma = BitmapFactory.decodeByteArray(
- data, 0, data.length);
- } finally {
- if (inputStream != null) {
- inputStream.close();
- }
- if (outputStream != null) {
- outputStream.close();
- }
- entity.consumeContent();
- }
- }
- } catch (IOException e) {
-
- getRequest.abort();
- Log.w(TAG, "Incorrect URL :" + url);
- e.printStackTrace();
- } finally {
- if (client != null) {
- client.close();
- }
- }
-
- return null;
- }
- }
这是两种做法,还有一些应用在下载的时候使用了线程池和消息队列MQ,对于图片的下载效果会更好一些
http://mobile.51cto.com/android-288600.htm