转自:http://www.devdiv.com/Android-%E4%BD%BF%E7%94%A8%E8%BD%AF%E5%BC%95%E7%94%A8%E6%9E%84%E5%BB%BA%E7%BC%93%E5%AD%98-thread-130476-1-1.html

============================================================================================

一、为什么要使用软引用

       在上面关于软引用的介绍中,已经提到了软引用的特性。使用SoftReference引用的对象会有很长的生命周期,只有当系统的内存不足的时候,才会去释放这些软引用对象。所以可以使用软引用来缓存一些比较昂贵的资源,比如获取的网络图片数据。

当应用从网络中获取网络图片数据时,用户完全有可能做一些重复性的操作去查看相同的图片信息。对于这样的问题,通常会有两种解决方法: 一种是把过去查看过的图片信息保存在内存中,每一个存储了图片信息的 Java 对象的生命周期都贯穿整个应用程序生命周期,另一种是当用户开始查看其他图片信息的时候,把存储了当前的图片信息的 Java 对象结束引用,使得垃圾收集线程可以回收其所占用的内存空间,当用户再次需要浏览该图片信息的时候,重新获取图片信息。

很显然,第一种实现方法将造成大量的内存浪费,而第二种实现的缺陷在于即使垃圾收集线程还没有进行垃圾收集,包含图片信息的对象仍然完好地保存在内存中,应用程序也要重新构建一个对象。

像访问磁盘文件、访问网络资源、查询数据库等操作都是影响应用程序执行性能的重要因素,如果能重新获取那些尚未被回收的 Java 对象的引用,必将减少不必要的访问,大大提高程序的运行速度。

这样看来,使用软引用是非常有必要的一件事情。

二、如何使用软引用

SoftReference 的特点是它的一个实例保存着一个 Java 对象的软引用,该软引用的存在不妨碍垃圾收集器线程对该 Java 对象的回收。也就是说,一旦SoftReference 保存着一个 Java 对象的软引用之后,在垃圾收集器线程对这个 Java 对象回收之前, SoftReference 类所提供的 get() 方法都会返回 这个Java 对象的强引用。另外,一旦垃圾线程回收该 Java 对象之后, get() 方法将返回 null 。

软引用的使用方法如下面的Java代码所示 :

1    MyObject aRef  =  new   MyObject();//创建一个对象
2    SoftReference aSoftRef = new SoftReference( aRef );//创建对象的软引用

上面的代码执行后,对于MyObject 对象,有两个引用路径,一个是来自 aSoftRef对象的软引用,一个来自变量 aRef 的强引用,所以 MyObject对象是强可及对象。紧跟着,可以使用下面的java的代码结束 aReference 对 MyObject 实例的强引用 :

1    aRef = null ;//断开对象的强引用

此后, MyObject 对象成为了软可及对象。如果垃圾收集线程进行内存垃圾收集,并不会因为有一个 SoftReference 对该对象的引用而始终保留该对象。 Java 虚拟机的垃圾收集线程对软可及对象和其他一般 Java 对象进行了区别对待 ,软可及对象的清理是由垃圾收集线程根据其特定算法按照内存需求决定的。也就是说,垃圾收集线程会在虚拟机抛出 OutOfMemoryError 之前回收软可及对象,而且虚拟机会尽可能优先回收长时间闲置不用的软可及对象,对那些刚刚构建的或刚刚使用过的“新”软可及对象会被虚拟机尽可能保留。如果想获取软引用中包含的对象,可以使用下面的Java代码:

1    MyObject anotherRef =(MyObject) aSoftRef .get();//通过软引用获取对象

在回收这些对象之前,可以通过上面的代码重新获得对该实例的强引用。而回收之后,当调用软引用的get() 方法时,返回的是 null 。

三、如何使用 ReferenceQueue

作为一个 Java 对象, SoftReference 对象除了具有保存软引用的特殊性之外,也具有 Java 对象的一般性。所以,当软可及对象被回收之后,虽然这个 SoftReference 对象的 get() 方法返回 null, 但这个 SoftReference 对象已经不再具有存在的价值,需要一个适当的清除机制,避免大量 SoftReference 对象带来的内存泄漏。在 java.lang.ref 包里还提供了 ReferenceQueue 。如果在创建 SoftReference 对象的时候,使用了带有一个 ReferenceQueue 对象作为参数的构造方法,如下面的Java代码 :

1    ReferenceQueue queue = new ReferenceQueue();//创建引用队列
2    SoftReference  ref = new SoftReference( aMyObject, queue );// 把引用加入到引用队列

   

当这个 SoftReference 所软引用的 aMyOhject 被垃圾收集器回收的同时,ref 所强引用的 SoftReference 对象被列入 ReferenceQueue 。也就是说, ReferenceQueue 中保存的对象是 Reference 对象,而且是已经失去了它所软引用的对象的 Reference 对象。另外从 ReferenceQueue 这个名字也可以看出,它是一个队列,当调用它的 poll() 方法的时候,如果这个队列中不是空队列,那么将返回队列前面的那个 Reference 对象。

在任何时候,都可以调用 ReferenceQueue 的 poll() 方法来检查是否有它所关心的非强可及对象被回收。如果队列为空,将返回一个 null, 否则该方法返回队列中最前面一个 Reference 对象。利用这个方法,可以检查哪个 SoftReference 所软引用的对象已经被回收。可以把这些失去所软引用的对象的 SoftReference 对象清除掉,如下面的Java代码所示。:

1    SoftReference ref = null ;
2    while ((ref = (EmployeeRef) q .poll()) != null ) {
3        // 清除 ref
4    }

   

四、实例分析

        理解了 Java中的引用机制之后就可以在Android中构造缓存器(cache)了,在Android中应用比较多的控件是ListView,通常会使用ListView显示网络数据列表,同时会包含图片缩略图,当数据量很大的时候,为了让用户能更流畅地流量信息,可以使用异步加载和缓存机制处理网络图片。
通过以上对于Java软引用类型的了解,可以知道使用软引用来构建缓存是比较合适的。虽然软引用能够延长数据对象的生命周期,但是对于移动设备来说,内存资源相对来说比较紧缺,仅使用软引用未必能达到最佳的缓存效果。通常会使用一些组合方式来进行数据缓存,最常用的是强引用、软引用加本地缓存的方式。

Android提供了一个AsyncTask类,它封装了基本的异步操作模型,只需要实现几个最基本的方法就可以很容易的实现异步加载图片,主要的方法是doInBackground方法和onPostExecute方法。AsyncTask类会启动一个新的线程执行doInBackground方法,所以我们所有的网络操作都应该在这个方法中实现,当doInBackground方法执行完成后,AsyncTask类会使用内置的Handler发送消息在主线程中执行onPostExecute方法,所以关于对UI的操作都应该放在onPostExecute方法中实现。

对于缓存的处理,主要思路是:在开始时,创建两个缓存区域:强引用缓存区域和软引用缓存区域。在强引用缓存区中保存有限的图片对象,根据LRU策略把一些最不常用的图片对象移到软引用缓存区,当缓存区域中都没有图片对象时从网络加载图片。完成后把图片数据保存到SDCard中,并根据LRU策略进行管理SDCard中保存的图片文件。

下图为ListView异步加载远程图片的流程图:

下面通过一个ListView的使用实例来说明如何在Android应用程序中使用异步加载图片,并且在内存和本地缓存它们。

第一步,首先建立一个Android工程,名称为AsyncListImage,由于应用需要访问网络所以需要修改AndroidManifest.xml文件,添加网络连接的权限,代码如下:

1
<uses-permission android:name="android.permission.INTERNET"/>

第二步,修改main.xml文件添加listview控件,并设置listview的一些基本属性信息,如下面的xml代码:

01
<?xml version="1.0" encoding="utf-8"?>
02
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
03android:orientation="vertical"
04
android:layout_width="fill_parent"
05android:layout_height="fill_parent">
06<ListView android:id="@+id/list"
07android:layout_width="fill_parent"
08android:layout_height="fill_parent"
09android:background="#ffffffff"
10android:cacheColorHint="#00000000"/>
11
</LinearLayout>

第三步,修改AsyncListImage Activity类并覆盖oncreate方法,初始化listview,并创建listview控件使用的Adapter。在AsyncListImage中定义了两种缓存区域A和B,A代表强引用缓存区域,B代表软引用缓存区域,由于使用强引用缓存区域保存数据只能保存一定的数量,而不能一直往里面存放,需要设置数据的过期时间、LRU等算法。这里有一个方法是把常用的数据放到缓存A中,不常用的放到另外一个缓存B中。当要获取数据时先从A中去获取,如果A中不存在那么再去B中获取。B中的数据主要是A中经过LRU生成的数据,这里的内存回收主要针对B内存,从而保持A中的数据可以有效的被命中。

下面是完整的Java代码:

  1 package com.devdiv.android.asynimagelist;
  2
  3 import java.io.File;
  4 import java.lang.ref.SoftReference;
  5 import java.util.HashMap;
  6 import java.util.LinkedHashMap;
  7 import java.util.concurrent.ConcurrentHashMap;
  8
  9 import android.app.Activity;
 10 import android.graphics.Bitmap;
 11 import android.os.Bundle;
 12 import android.util.Log;
 13 import android.view.View;
 14 import android.view.ViewGroup;
 15 import android.widget.BaseAdapter;
 16 import android.widget.ImageView;
 17 import android.widget.ListView;
 18 import android.widget.ImageView.ScaleType;
 19
 20 @SuppressWarnings("serial")
 21 public class AsyncListImage extends Activity implements RemoteImageCallback {
 22         private ListView list;
 23         private static final String TAG = AsyncListImage.class.getSimpleName();
 24         private static final int HARD_CACHE_CAPACITY = 10;
 25
 26         private final HashMap<String, Bitmap> mHardBitmapCache = new LinkedHashMap<String, Bitmap>(HARD_CACHE_CAPACITY / 2, 0.75f, true) {
 27
 28                 @Override
 29         protected boolean removeEldestEntry(LinkedHashMap.Entry<String, Bitmap> eldest) {
 30             if (size() > HARD_CACHE_CAPACITY) {
 31                //当map的size大于10时,把最近不常用的key放到mSoftBitmapCache中,从而保证mHardBitmapCache的效率
 32                mSoftBitmapCache.put(eldest.getKey(), new SoftReference<Bitmap>(eldest.getValue()));
 33                 return true;
 34             } else
 35                 return false;
 36         }
 37     };
 38
 39         /**
 40          *当mHardBitmapCache的key大于10的时候,会根据LRU算法把最近没有被使用的key放入到这个缓存中。
 41          * Bitmap使用了SoftReference,当内存空间不足时,此cache中的bitmap会被垃圾回收掉
 42          */
 43         private final static ConcurrentHashMap<String, SoftReference<Bitmap>> mSoftBitmapCache = new ConcurrentHashMap<String, SoftReference<Bitmap>>(
 44                         HARD_CACHE_CAPACITY / 2);
 45
 46         @Override
 47         public void onCreate(Bundle savedInstanceState) {
 48                 super.onCreate(savedInstanceState);
 49                 setContentView(R.layout.main);
 50                 list = (ListView) findViewById(R.id.list);
 51
 52                 initCacheDir();
 53
 54                 MyListAdapter adapter = new MyListAdapter();
 55                 list.setAdapter(adapter);
 56         }
 57
 58         private void initCacheDir() {
 59                 String cacheDir = "/data/data/com.devdiv.android.asynimagelist/files/caches";
 60                 File f = new File(cacheDir);
 61                 if (!f.exists()) {
 62                         f.mkdirs();
 63                 }
 64         }
 65
 66         private class MyListAdapter extends BaseAdapter {
 67                 private String[] urls = new String[] {
 68 "http://www.icosky.com/icon/thumbnails/System/Sleek%20XP%20Basic/Add%20Icon.jpg",
 69 "http://www.icosky.com/icon/thumbnails/System/Sleek%20XP%20Basic/Adobe%20Illustator%20Icon.jpg",
 70 "http://www.icosky.com/icon/thumbnails/System/Sleek%20XP%20Basic/Attach%20Icon.jpg",
 71 "http://www.icosky.com/icon/thumbnails/System/Sleek%20XP%20Basic/Applications%20Cascade%20Icon.jpg",
 72 "http://www.icosky.com/icon/thumbnails/System/Sleek%20XP%20Basic/Administrator%20Icon.jpg",
 73 "http://www.icosky.com/icon/thumbnails/System/Sleek%20XP%20Basic/Clients%20Icon.jpg",
 74 "http://www.icosky.com/icon/thumbnails/System/Sleek%20XP%20Basic/Coinstack%20Icon.jpg",
 75 "http://www.icosky.com/icon/thumbnails/System/Sleek%20XP%20Basic/Download%20Icon.jpg",
 76 "http://www.icosky.com/icon/thumbnails/System/Sleek%20XP%20Basic/Help%20Icon.jpg",
 77 "http://www.icosky.com/icon/thumbnails/System/Sleek%20XP%20Basic/Home%20Icon.jpg",
 78 "http://www.icosky.com/icon/thumbnails/System/Sleek%20XP%20Basic/Pen%20Icon.jpg",
 79 "http://www.icosky.com/icon/thumbnails/System/Sleek%20XP%20Basic/Statistics%20Icon.jpg"
 80                 };
 81
 82                 @Override
 83                 public int getCount() {
 84                         return urls.length;
 85                 }
 86
 87                 @Override
 88                 public String getItem(int position) {
 89                         return urls[position];
 90                 }
 91
 92                 @Override
 93                 public long getItemId(int position) {
 94                         return position;
 95                 }
 96
 97                 @Override
 98                 public View getView(int position, View convertView, ViewGroup parent) {
 99                         if (convertView == null) {
100                                 convertView = new ImageView(AsyncListImage.this);
101                         }
102                         ImageView iv = (ImageView)convertView;
103                         iv.setScaleType(ScaleType.FIT_START);
104                         Bitmap bitmap = getBitmapFromCache(getItem(position));
105                         if (bitmap == null) {
106                                 iv.setImageResource(R.drawable.default_image);
107                                 iv.setTag(getItem(position));
108                                 new ImageDownloaderTask(AsyncListImage.this).execute(new String[]{getItem(position)});
109                         } else {
110                                 iv.setImageBitmap(bitmap);
111                         }
112                         iv = null;
113                         return convertView;
114                 }
115
116         }
117
118         /**
119      * 从缓存中获取图片
120      */
121     private Bitmap getBitmapFromCache(String url) {
122         // 先从mHardBitmapCache缓存中获取
123         synchronized (mHardBitmapCache) {
124             final Bitmap bitmap = mHardBitmapCache.get(url);
125             if (bitmap != null) {
126                 //如果找到的话,把元素移到linkedhashmap的最前面,从而保证在LRU算法中是最后被删除
127                 mHardBitmapCache.remove(url);
128                 Log.d(TAG, "move bitmap to the head of linkedhashmap:" + url);
129                 mHardBitmapCache.put(url,bitmap);
130                 return bitmap;
131             }
132         }
133         //如果mHardBitmapCache中找不到,到mSoftBitmapCache中找
134         SoftReference<Bitmap> bitmapReference = mSoftBitmapCache.get(url);
135         if (bitmapReference != null) {
136             final Bitmap bitmap = bitmapReference.get();
137             if (bitmap != null) {
138                     Log.d(TAG, "get bitmap from mSoftBitmapCache with key:" + url);
139                 return bitmap;
140             } else {
141                 mSoftBitmapCache.remove(url);
142                 Log.d(TAG, "remove bitmap with key:" + url);
143             }
144         }
145         return null;
146     }
147
148         @Override
149         public void onComplete(String url, Bitmap bitmap) {
150                 Log.d(TAG, "onComplete after got bitmap from remote with key:" + url);
151                 ImageView iv = (ImageView)list.findViewWithTag(url);
152                 if (iv != null) {
153                         iv.setImageBitmap(bitmap);
154                         mHardBitmapCache.put(url, bitmap);
155                 }
156         }
157
158 }

第四步,定义AsyncTask 类的子类ImageDownloaderTask类并覆盖doInBackground 方法和onPostExecute方法。在doInBackground方法中进行网络操作和文件操作,在onPostExecute方法中执行回调函数,把获取的bitmap数据发送到UI线程与ListView中的imageView进行关联,Java代码如下。

  1 package com.devdiv.android.asynimagelist;
  2
  3 import java.io.BufferedOutputStream;
  4 import java.io.ByteArrayOutputStream;
  5 import java.io.Closeable;
  6 import java.io.File;
  7 import java.io.FileNotFoundException;
  8 import java.io.FileOutputStream;
  9 import java.io.IOException;
 10 import java.io.InputStream;
 11 import java.io.OutputStream;
 12 import java.lang.ref.WeakReference;
 13 import java.util.Arrays;
 14 import java.util.Comparator;
 15
 16 import org.apache.http.HttpEntity;
 17 import org.apache.http.HttpResponse;
 18 import org.apache.http.HttpStatus;
 19 import org.apache.http.client.methods.HttpGet;
 20 import org.apache.http.impl.client.DefaultHttpClient;
 21
 22 import android.graphics.Bitmap;
 23 import android.graphics.BitmapFactory;
 24 import android.os.AsyncTask;
 25 import android.os.Environment;
 26 import android.os.StatFs;
 27 import android.util.Log;
 28
 29 public class ImageDownloaderTask extends AsyncTask<String, Void, Bitmap> {
 30         private static String TAG = ImageDownloaderTask.class.getSimpleName();
 31         private static final int IO_BUFFER_SIZE = 4 * 1024;
 32         private static final int MB = 1024 * 1024;
 33         private static final int CACHE_SIZE = 1024 * 1024;
 34         private static final int mTimeDiff = 5 * 24 * 60 * 60 * 1000;
 35         private static final int FREE_SD_SPACE_NEEDED_TO_CACHE = 30;
 36         private static final String WHOLESALE_CONV = "/data/data/com.devdiv.android.asynimagelist/files/caches";
 37         private String url;
 38         private final WeakReference<AsyncListImage> activityReference;
 39
 40         public ImageDownloaderTask(AsyncListImage activity) {
 41                 activityReference = new WeakReference<AsyncListImage>(activity);
 42         }
 43
 44         @Override
 45         protected Bitmap doInBackground(String... params) {
 46                 url = params[0];
 47                 String filename = convertUrlToFileName(url);
 48                 String dir = getDirectory(filename);
 49                 File file = new File(dir + "/" + filename);
 50                 if (file.exists()) {
 51                         removeExpiredCache(dir, filename);
 52                         updateFileTime(dir, filename);
 53                         Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
 54                         if (bitmap != null)
 55                                 return bitmap;
 56                 }
 57
 58                 final DefaultHttpClient client = new DefaultHttpClient();
 59
 60                 final HttpGet getRequest = new HttpGet(url);
 61                 try {
 62                         HttpResponse response = client.execute(getRequest);
 63                         final int statusCode = response.getStatusLine().getStatusCode();
 64                         if (statusCode != HttpStatus.SC_OK) {
 65                                 Log.w(TAG, "从" + url + "中下载图片时出错!,错误码:" + statusCode);
 66                                 return null;
 67                         }
 68                         final HttpEntity entity = response.getEntity();
 69                         if (entity != null) {
 70                                 InputStream inputStream = null;
 71                                 OutputStream outputStream = null;
 72                                 try {
 73                                         inputStream = entity.getContent();
 74                                         final ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
 75                                         outputStream = new BufferedOutputStream(dataStream,
 76                                                         IO_BUFFER_SIZE);
 77                                         copy(inputStream, outputStream);
 78                                         outputStream.flush();
 79                                         final byte[] data = dataStream.toByteArray();
 80                                         final Bitmap bitmap = BitmapFactory.decodeByteArray(data,
 81                                                         0, data.length);
 82
 83                                         saveBmpToSd(bitmap, url);
 84
 85                                         return bitmap;
 86                                 } finally {
 87                                         closeStream(inputStream);
 88                                         closeStream(outputStream);
 89                                         entity.consumeContent();
 90                                 }
 91                         }
 92                 } catch (IOException e) {
 93                         getRequest.abort();
 94                         Log.w(TAG, "I/O error while retrieving bitmap from " + url, e);
 95                 } catch (IllegalStateException e) {
 96                         getRequest.abort();
 97                         Log.w(TAG, "Incorrect URL:" + url);
 98                 } catch (Exception e) {
 99                         getRequest.abort();
100                         Log.w(TAG, "Error while retrieving bitmap from " + url, e);
101                 }
102                 return null;
103         }
104
105         @Override
106         protected void onPostExecute(Bitmap result) {
107                 super.onPostExecute(result);
108                 AsyncListImage act = activityReference.get();
109                 if (act != null && result != null) {
110                         act.onComplete(url, result);
111                 }
112         }
113
114
115     /**
116      * Copy the content of the input stream into the output stream, using a temporary
117      * byte array buffer whose size is defined by {@link #IO_BUFFER_SIZE}.
118      *
119      * @param in The input stream to copy from.
120      * @param out The output stream to copy to.
121      *
122      * @throws java.io.IOException If any error occurs during the copy.
123      */
124     public static void copy(InputStream in, OutputStream out) throws IOException {
125         byte[] b = new byte[IO_BUFFER_SIZE];
126         int read;
127         while ((read = in.read(b)) != -1) {
128             out.write(b, 0, read);
129         }
130     }
131
132     /**
133      * Closes the specified stream.
134      *
135      * @param stream The stream to close.
136      */
137     public static void closeStream(Closeable stream) {
138         if (stream != null) {
139             try {
140                 stream.close();
141             } catch (IOException e) {
142                 android.util.Log.e(TAG, "Could not close stream", e);
143             }
144         }
145     }
146
147     private void saveBmpToSd(Bitmap bm, String url) {
148                 if (bm == null) {
149                         Log.w(TAG, " trying to savenull bitmap");
150                         return;
151                 }
152                 // 判断sdcard上的空间
153                 if (FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
154                         Log.w(TAG, "Low free space onsd, do not cache");
155                         removeCache(WHOLESALE_CONV);
156                         return;
157                 }
158                 String filename = convertUrlToFileName(url);
159                 String dir = getDirectory(filename);
160                 File file = new File(dir + "/" + filename);
161                 try {
162                         file.createNewFile();
163                         OutputStream outStream = new FileOutputStream(file);
164                         bm.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
165                         outStream.flush();
166                         outStream.close();
167                         Log.i(TAG, "Image saved tosd");
168                 } catch (FileNotFoundException e) {
169                         Log.w(TAG, "FileNotFoundException");
170                 } catch (IOException e) {
171                         Log.w(TAG, "IOException");
172                 }
173         }
174
175         private String convertUrlToFileName(String url) {
176                 int lastIndex = url.lastIndexOf('/');
177                 return url.substring(lastIndex + 1);
178         }
179
180         private String getDirectory(String filename) {
181                 return WHOLESALE_CONV;
182         }
183
184         /**
185          * 计算sdcard上的剩余空间
186          *
187          * @return
188          */
189         private int freeSpaceOnSd() {
190                 StatFs stat = new StatFs(Environment.getExternalStorageDirectory()
191                                 .getPath());
192                 double sdFreeMB = ((double) stat.getAvailableBlocks() * (double) stat
193                                 .getBlockSize())
194                                 / MB;
195                 return (int) sdFreeMB;
196         }
197
198         /**
199          * 修改文件的最后修改时间
200          *
201          * @param dir
202          * @param fileName
203          */
204         private void updateFileTime(String dir, String fileName) {
205                 File file = new File(dir, fileName);
206                 long newModifiedTime = System.currentTimeMillis();
207                 file.setLastModified(newModifiedTime);
208         }
209
210         /**
211          *计算存储目录下的文件大小,
212          * 当文件总大小大于规定的CACHE_SIZE或者sdcard剩余空间小于FREE_SD_SPACE_NEEDED_TO_CACHE的规定
213          * 那么删除40%最近没有被使用的文件
214          *
215          * @param dirPath
216          * @param filename
217          */
218         private void removeCache(String dirPath) {
219                 File dir = new File(dirPath);
220                 File[] files = dir.listFiles();
221                 if (files == null) {
222                         return;
223                 }
224                 int dirSize = 0;
225                 for (int i = 0; i < files.length; i++) {
226                         if (files.getName().contains(WHOLESALE_CONV)) {
227                                 dirSize += files.length();
228                         }
229                 }
230                 if (dirSize > CACHE_SIZE * MB
231                                 || FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
232                         int removeFactor = (int) ((0.4 * files.length) + 1);
233
234                         Arrays.sort(files, new FileLastModifSort());
235
236                         Log.i(TAG, "Clear some expiredcache files ");
237
238                         for (int i = 0; i < removeFactor; i++) {
239
240                                 if (files.getName().contains(WHOLESALE_CONV)) {
241
242                                         files.delete();
243
244                                 }
245
246                         }
247
248                 }
249
250         }
251
252         /**
253          * TODO 根据文件的最后修改时间进行排序 *
254          */
255         class FileLastModifSort implements Comparator<File> {
256                 public int compare(File arg0, File arg1) {
257                         if (arg0.lastModified() > arg1.lastModified()) {
258                                 return 1;
259                         } else if (arg0.lastModified() == arg1.lastModified()) {
260                                 return 0;
261                         } else {
262                                 return -1;
263                         }
264                 }
265         }
266
267         /**
268          * 删除过期文件
269          *
270          * @param dirPath
271          * @param filename
272          */
273         private void removeExpiredCache(String dirPath, String filename) {
274
275                 File file = new File(dirPath, filename);
276
277                 if (System.currentTimeMillis() - file.lastModified() > mTimeDiff) {
278
279                         Log.i(TAG, "Clear some expiredcache files ");
280
281                         file.delete();
282
283                 }
284
285         }
286 }

第五步、运行工程之后,运行效果如下图示。

运行工程,最开始时,ListView使用默认的图片填充imageview。

然后使用异步方式获取网络图片,当获取到网络图片后,使用最新的网络图片替换默认图片。

当用户拖动ListView,显示效果如下图所示:

再次运行工程后,加载图片的速度会非常块,因为程序在手机内存中存储了相应的图片资源,直接加载这些资源就好了,不需要再访问网络。

下图是在DDMS的File Explorer中的显示:

转载于:https://www.cnblogs.com/elefish/archive/2013/02/15/2912809.html

android使用软引用构建缓存相关推荐

  1. Android 通过软引用实现图片缓存,防止内存溢出

    public class BitmapCache {static private BitmapCache cache;/** 用于Chche内容的存储 */private Hashtable<I ...

  2. Android使用软引用和弱引用

    Java从JDK1.2版本开始,就把对象的引用分为四种级别,从而使程序能更加灵活的控制对象的生命周期.这四种级别由高到低依次为:强引用.软引用.弱引用和虚引用. 这里重点介绍一下软引用和弱引用. 如果 ...

  3. HashMap + 软引用进行缓存

    一.强引用 强引用的特点: 可以直接访问目标对象. 所指向的对象在任何时候都不会被系统回收.JVM宁愿抛出OOM异常,也不会回收强引用所指向的对象. 可能导致内存泄漏. //强引用 User user ...

  4. Java 强引用与软引用以及弱引用,虚引用

    1.对象的强.软.弱和虚引用 在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象.也就是说,只有对象处于可触及(reachable)状态,程序才能使 用它.从JDK ...

  5. Java四种引用包括强引用,软引用,弱引用,虚引用。

    强引用: 只要引用存在,垃圾回收器永远不会回收 Object obj = new Object(); //可直接通过obj取得对应的对象 如obj.equels(new Object()); 而这样 ...

  6. 弱引用和软引用的应用场景

    1.利用软引用和弱引用解决OOM问题 假如有一个应用需要读取大量的本地图片,如果每次读取图片都从硬盘读取,则会严重影响性能,但是如果全部加载到内存中,又可能造成内存溢出,此时使用软引用可以解决这个问题 ...

  7. java基础-----弱引用,软引用,强引用,虚引用

    转自:http://www.cnblogs.com/blogoflee/archive/2012/03/22/2411124.html 1.概述 在JDK1.2以前的版本中,当一个对象不被任何变量引用 ...

  8. 简单直白教你理解Java中四大引用强引用,软引用,弱引用,虚引用

    我属于自学型的,所以知识不够系统,只能是一边儿工作一边查漏补缺,在此要对那些写技术文章的人由衷的说句谢谢,谢谢各位大神们的分享 ONE,强引用(StrongReference) 概念介绍: 在此说明一 ...

  9. Android 图片缓存之内存缓存技术LruCache,软引用

    Android 图片缓存之内存缓存技术LruCache,软引用

最新文章

  1. 当当网新用户注册界面——界面源码
  2. 2020年中国无人经济市场研究报告
  3. php上js实现ajax请求,原生JS如何实现Ajax通过POST方式与PHP进行交互的方法
  4. 古典绘画水墨文化艺术插图手绘合集,再也不愁没有设计灵感!
  5. javascript中构造StringBuffer实例
  6. 2017 计蒜之道 初赛 第五场 A. UCloud 机房的网络搭建
  7. android代码设置点击涟漪,android – 为自定义CompoundButton添加涟漪效果
  8. 使用 Litho 改进 News Feed 上的 Android 视频表现
  9. Google Code Jam 全球编程挑战赛来袭,报名倒计时!
  10. 国产GP232RL 串口芯片完美兼容FT232RL 引脚开发资料
  11. P2339 [USACO04OPEN]Turning in Homework G 笔记/题解
  12. CMD命令下载远程文件
  13. HDOJ 1280 前m大的数(水题)
  14. Java内存模型(JMM)详解-可见性volatile
  15. Swing实现五子棋
  16. 11月全球浏览器份额之争:Chrome与Firefox均被蚕食
  17. WinRAR压缩时排除文件或目录
  18. html百度天气查询api,百度提供天气预报查询接口API
  19. Android Studio 实现跑马灯微项目实例
  20. Android获取手机设备识别码(IMEI)和手机号码

热门文章

  1. MySQL连接问题【如何解决MySQL连接超时关闭】
  2. c#如何操作excel文件、Interior.ColorIndex 色彩列表
  3. linux 擦脚印工具,linux一步一脚印---rm命令
  4. java 静态实例_Java实例和静态方法
  5. 5 加盐_工业软水处理器定期加盐的标准
  6. 大唐杯5g题库_“大唐杯”全国大学生移动通信技术大赛暨5G教育创新论坛成功举行...
  7. leetcod算法题--找出星型图的中心节点
  8. leetcode算法题--合并区间
  9. 如何用python做词云图_科学网—如何用Python做词云?(基础篇视频教程) - 王树义的博文...
  10. P2689 东南西北