Android(安卓)获取远程图片与本地图片缓存
Android 获取远程图片与本地图片缓存
标签: Android图片缓存 2013-04-11 10:01 3438人阅读 评论(0) 收藏 举报 分类: Android App(30)版权声明:本文为博主原创文章,未经博主允许不得转载。
[java] view plain copy print ?
- "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的代码:
[java] view plain copy print ?
- private void saveBmpToSd(Bitmap bm, String url) {
- if (bm == null) {
- Log.w(TAG, " trying to savenull bitmap");
- return;
- }
- // 判断sdcard上的空间
- 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上的空间:
[java] view plain copy print ?
- /**
- * 计算sdcard上的剩余空间
- *
- * @return
- */
- private int freeSpaceOnSd() {
- // TODO Auto-generated method stub
- StatFs stat = new StatFs(Environment.getExternalStorageDirectory()
- .getPath());
- double sdFreeMB = ((double) stat.getAvailableBlocks() * (double) stat
- .getBlockSize()); // MB
- return (int) sdFreeMB;
- }
修改文件的最后修改时间:
[java] view plain copy print ?
- /**
- * 修改文件的最后修改时间
- *
- * @param dir
- * @param fileName
- */
- private void updateFileTime(String dir, String fileName) {
- File file = new File(dir, fileName);
- long newModifiedTime = System.currentTimeMillis();
- file.setLastModified(newModifiedTime);
- }
本地缓存优化:
[java] view plain copy print ?
- /**
- * 计算储存目录下的文件大小,当文件的总大小超过规定的CACHE_SIZE,
- * 或者是Sdcard剩余空间小于FREE_SD_SPACE_NEEDED_TO_CACHE 的规定 ,那么删除40%最近没有使用的图片
- *
- * @param dirPath
- */
- 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();
- }
- }
- }
- }
- /**
- * 删除过期文件
- * @param dirPath
- * @param fileName
- */
- 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();
- }
- }
文件使用时间排序:
[java] view plain copy print ?
- /**
- * 根据文件的最后修改时间进行排序
- * @author huanglong
- *
- */
- 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缓存:
[java] view plain copy print ?
- // 定义A缓存
- private final HashMap
mHardBItmapCache = new LinkedHashMap( - HARD_CACHE_CAPACITY / 2, 0.75f, true) {
- @Override
- protected boolean removeEldestEntry(
- java.util.Map.Entry
eldest) { - // TODO Auto-generated method stub
- if (size() > HARD_CACHE_CAPACITY) {
- // 保证map的size大于30时候,把最不常用的key放到mSoftBitmapCache中,从而保证mHardBitmapCache的效率
- mSoftBitmapCache.put(eldest.getKey(),
- new SoftReference
(eldest.getValue())); - return true;
- } else {
- return false;
- }
- }
- };
定义B缓存:
[java] view plain copy print ?
- // 定义B缓存
- // 当mHardBitmapCache的key大于30的时候,会根据LRU算法把最近没有使用的Key放入到缓存中
- // Bitmap 使用了SoftReference 当内存不足的时候,此时cache中的bitmap会被垃圾回收掉
- private final static ConcurrentHashMap
> mSoftBitmapCache = new ConcurrentHashMap>( - HARD_CACHE_CAPACITY / 2);
从缓存中获取数据:
[java] view plain copy print ?
- /**
- * 从缓存中获得数据
- *
- * @param url
- * @return
- */
- private Bitmap getBitmapFromeCache(String url) {
- // 先从mHardBitmapCache缓存中获取
- synchronized (mHardBItmapCache) {
- final Bitmap bitmap = mHardBItmapCache.get(url);
- if (bitmap != null) {
- // 如果找到的话,把元素移动到linkedHashMap的最前面,从而保证LRU算法中是最后被删除的
- mHardBItmapCache.remove(url);
- mHardBItmapCache.put(url, bitmap);
- return bitmap;
- }
- }
- // 如果mHardBitmapCache中找不到,到mSoftBitmapCache中找
- SoftReference
bitmapReference = mSoftBitmapCache.get(url); - if (bitmapReference != null) {
- final Bitmap bitmap = bitmapReference.get();
- if (bitmap != null) {
- return bitmap;
- } else {
- mSoftBitmapCache.remove(url);
- }
- }
- return null;
- }
如果缓存不存在,那么就只能去服务器下载:
[java] view plain copy print ?
- // 如果缓存中不存在那么就只能去服务器下载
- 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) {
- // TODO Auto-generated method stub
- 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) {
- // TODO Auto-generated catch block
- 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
更多相关文章
- 浅谈android的selector背景选择器
- Android与服务器端数据交互
- android 异步获取图片
- 【Android(安卓)Demo】简单手机通讯录
- Android大图片内存清理
- android Lru图片缓存管理方案
- Android(安卓)如何在关于手机界面添加个图片
- Android之——清理手机SD卡缓存
- android 7种网络连接方式--IT蓝豹