轉(zhuǎn)自: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
============================================================================================
一、為什么要使用軟引用
? ?? ? 在上面關(guān)于軟引用的介紹中,已經(jīng)提到了軟引用的特性。使用SoftReference引用的對象會有很長的生命周期,只有當(dāng)系統(tǒng)的內(nèi)存不足的時(shí)候,才會去釋放這些軟引用對象。所以可以使用軟引用來緩存一些比較昂貴的資源,比如獲取的網(wǎng)絡(luò)圖片數(shù)據(jù)。
? ?? ???當(dāng)應(yīng)用從網(wǎng)絡(luò)中獲取網(wǎng)絡(luò)圖片數(shù)據(jù)時(shí),用戶完全有可能做一些重復(fù)性的操作去查看相同的圖片信息。對于這樣的問題,通常會有兩種解決方法: 一種是把過去查看過的圖片信息保存在內(nèi)存中,每一個存儲了圖片信息的 Java 對象的生命周期都貫穿整個應(yīng)用程序生命周期,另一種是當(dāng)用戶開始查看其他圖片信息的時(shí)候,把存儲了當(dāng)前的圖片信息的 Java 對象結(jié)束引用,使得垃圾收集線程可以回收其所占用的內(nèi)存空間,當(dāng)用戶再次需要瀏覽該圖片信息的時(shí)候,重新獲取圖片信息。
? ?? ???很顯然,第一種實(shí)現(xiàn)方法將造成大量的內(nèi)存浪費(fèi),而第二種實(shí)現(xiàn)的缺陷在于即使垃圾收集線程還沒有進(jìn)行垃圾收集,包含圖片信息的對象仍然完好地保存在內(nèi)存中,應(yīng)用程序也要重新構(gòu)建一個對象。
? ?? ???像訪問磁盤文件、訪問網(wǎng)絡(luò)資源、查詢數(shù)據(jù)庫等操作都是影響應(yīng)用程序執(zhí)行性能的重要因素,如果能重新獲取那些尚未被回收的 Java 對象的引用,必將減少不必要的訪問,大大提高程序的運(yùn)行速度。
? ?? ???這樣看來,使用軟引用是非常有必要的一件事情。
二、如何使用軟引用
? ?? ???SoftReference 的特點(diǎn)是它的一個實(shí)例保存著一個 Java 對象的軟引用,該軟引用的存在不妨礙垃圾收集器線程對該 Java 對象的回收。也就是說,一旦SoftReference 保存著一個 Java 對象的軟引用之后,在垃圾收集器線程對這個 Java 對象回收之前, SoftReference 類所提供的 get() 方法都會返回 這個Java 對象的強(qiáng)引用。另外,一旦垃圾線程回收該 Java 對象之后, get() 方法將返回 null 。
? ?? ???軟引用的使用方法如下面的Java代碼所示 :
1 MyObject aRef =
new MyObject();
//創(chuàng)建一個對象
2 SoftReference aSoftRef =
new SoftReference( aRef );
//創(chuàng)建對象的軟引用
? ?
? ?? ???上面的代碼執(zhí)行后,對于MyObject 對象,有兩個引用路徑,一個是來自 aSoftRef對象的軟引用,一個來自變量 aRef 的強(qiáng)引用,所以 MyObject對象是強(qiáng)可及對象。緊跟著,可以使用下面的java的代碼結(jié)束 aReference 對 MyObject 實(shí)例的強(qiáng)引用 :
1 aRef =
null ;
//斷開對象的強(qiáng)引用
? ?? ???此后, MyObject 對象成為了軟可及對象。如果垃圾收集線程進(jìn)行內(nèi)存垃圾收集,并不會因?yàn)橛幸粋€ SoftReference 對該對象的引用而始終保留該對象。 Java 虛擬機(jī)的垃圾收集線程對軟可及對象和其他一般 Java 對象進(jìn)行了區(qū)別對待 ,軟可及對象的清理是由垃圾收集線程根據(jù)其特定算法按照內(nèi)存需求決定的。也就是說,垃圾收集線程會在虛擬機(jī)拋出 OutOfMemoryError 之前回收軟可及對象,而且虛擬機(jī)會盡可能優(yōu)先回收長時(shí)間閑置不用的軟可及對象,對那些剛剛構(gòu)建的或剛剛使用過的“新”軟可及對象會被虛擬機(jī)盡可能保留。如果想獲取軟引用中包含的對象,可以使用下面的Java代碼:
1 MyObject anotherRef =(MyObject) aSoftRef .get();
//通過軟引用獲取對象 ?
? ??
? ?? ???在回收這些對象之前,可以通過上面的代碼重新獲得對該實(shí)例的強(qiáng)引用。而回收之后,當(dāng)調(diào)用軟引用的get() 方法時(shí),返回的是 null 。
三、如何使用 ReferenceQueue
? ?? ???作為一個 Java 對象, SoftReference 對象除了具有保存軟引用的特殊性之外,也具有 Java 對象的一般性。所以,當(dāng)軟可及對象被回收之后,雖然這個 SoftReference 對象的 get() 方法返回 null, 但這個 SoftReference 對象已經(jīng)不再具有存在的價(jià)值,需要一個適當(dāng)?shù)那宄龣C(jī)制,避免大量 SoftReference 對象帶來的內(nèi)存泄漏。在 java.lang.ref 包里還提供了 ReferenceQueue 。如果在創(chuàng)建 SoftReference 對象的時(shí)候,使用了帶有一個 ReferenceQueue 對象作為參數(shù)的構(gòu)造方法,如下面的Java代碼 :
1 ReferenceQueue queue =
new ReferenceQueue();
//創(chuàng)建引用隊(duì)列
2 SoftReference ref =
new SoftReference( aMyObject, queue );
// 把引用加入到引用隊(duì)列 ?
? ?? ???當(dāng)這個 SoftReference 所軟引用的 aMyOhject 被垃圾收集器回收的同時(shí),ref 所強(qiáng)引用的 SoftReference 對象被列入 ReferenceQueue 。也就是說, ReferenceQueue 中保存的對象是 Reference 對象,而且是已經(jīng)失去了它所軟引用的對象的 Reference 對象。另外從 ReferenceQueue 這個名字也可以看出,它是一個隊(duì)列,當(dāng)調(diào)用它的 poll() 方法的時(shí)候,如果這個隊(duì)列中不是空隊(duì)列,那么將返回隊(duì)列前面的那個 Reference 對象。
? ?? ???在任何時(shí)候,都可以調(diào)用 ReferenceQueue 的 poll() 方法來檢查是否有它所關(guān)心的非強(qiáng)可及對象被回收。如果隊(duì)列為空,將返回一個 null, 否則該方法返回隊(duì)列中最前面一個 Reference 對象。利用這個方法,可以檢查哪個 SoftReference 所軟引用的對象已經(jīng)被回收。可以把這些失去所軟引用的對象的 SoftReference 對象清除掉,如下面的Java代碼所示。:
1 SoftReference ref =
null ;
2
while ((ref = (EmployeeRef) q .poll()) !=
null ) {
3
// 清除 ref
4 }
四、實(shí)例分析
? ?? ???理解了 Java中的引用機(jī)制之后就可以在Android中構(gòu)造緩存器(cache)了,在Android中應(yīng)用比較多的控件是ListView,通常會使用ListView顯示網(wǎng)絡(luò)數(shù)據(jù)列表,同時(shí)會包含圖片縮略圖,當(dāng)數(shù)據(jù)量很大的時(shí)候,為了讓用戶能更流暢地流量信息,可以使用異步加載和緩存機(jī)制處理網(wǎng)絡(luò)圖片。
通過以上對于Java軟引用類型的了解,可以知道使用軟引用來構(gòu)建緩存是比較合適的。雖然軟引用能夠延長數(shù)據(jù)對象的生命周期,但是對于移動設(shè)備來說,內(nèi)存資源相對來說比較緊缺,僅使用軟引用未必能達(dá)到最佳的緩存效果。通常會使用一些組合方式來進(jìn)行數(shù)據(jù)緩存,最常用的是強(qiáng)引用、軟引用加本地緩存的方式。
? ?? ???Android提供了一個AsyncTask類,它封裝了基本的異步操作模型,只需要實(shí)現(xiàn)幾個最基本的方法就可以很容易的實(shí)現(xiàn)異步加載圖片,主要的方法是doInBackground方法和onPostExecute方法。AsyncTask類會啟動一個新的線程執(zhí)行doInBackground方法,所以我們所有的網(wǎng)絡(luò)操作都應(yīng)該在這個方法中實(shí)現(xiàn),當(dāng)doInBackground方法執(zhí)行完成后,AsyncTask類會使用內(nèi)置的Handler發(fā)送消息在主線程中執(zhí)行onPostExecute方法,所以關(guān)于對UI的操作都應(yīng)該放在onPostExecute方法中實(shí)現(xiàn)。
? ?? ???對于緩存的處理,主要思路是:在開始時(shí),創(chuàng)建兩個緩存區(qū)域:強(qiáng)引用緩存區(qū)域和軟引用緩存區(qū)域。在強(qiáng)引用緩存區(qū)中保存有限的圖片對象,根據(jù)LRU策略把一些最不常用的圖片對象移到軟引用緩存區(qū),當(dāng)緩存區(qū)域中都沒有圖片對象時(shí)從網(wǎng)絡(luò)加載圖片。完成后把圖片數(shù)據(jù)保存到SDCard中,并根據(jù)LRU策略進(jìn)行管理SDCard中保存的圖片文件。
? ?? ???下圖為ListView異步加載遠(yuǎn)程圖片的流程圖:
?
下面通過一個ListView的使用實(shí)例來說明如何在Android應(yīng)用程序中使用異步加載圖片,并且在內(nèi)存和本地緩存它們。
? ?? ???第一步,首先建立一個Android工程,名稱為AsyncListImage,由于應(yīng)用需要訪問網(wǎng)絡(luò)所以需要修改AndroidManifest.xml文件,添加網(wǎng)絡(luò)連接的權(quán)限,代碼如下:
1
<uses-permission android:name="android.permission.INTERNET"/> ??? ???第二步,修改main.xml文件添加listview控件,并設(shè)置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,并創(chuàng)建listview控件使用的Adapter。在AsyncListImage中定義了兩種緩存區(qū)域A和B,A代表強(qiáng)引用緩存區(qū)域,B代表軟引用緩存區(qū)域,由于使用強(qiáng)引用緩存區(qū)域保存數(shù)據(jù)只能保存一定的數(shù)量,而不能一直往里面存放,需要設(shè)置數(shù)據(jù)的過期時(shí)間、LRU等算法。這里有一個方法是把常用的數(shù)據(jù)放到緩存A中,不常用的放到另外一個緩存B中。當(dāng)要獲取數(shù)據(jù)時(shí)先從A中去獲取,如果A中不存在那么再去B中獲取。B中的數(shù)據(jù)主要是A中經(jīng)過LRU生成的數(shù)據(jù),這里的內(nèi)存回收主要針對B內(nèi)存,從而保持A中的數(shù)據(jù)可以有效的被命中。
? ?? ???下面是完整的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 //當(dāng)map的size大于10時(shí),把最近不常用的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 *當(dāng)mHardBitmapCache的key大于10的時(shí)候,會根據(jù)LRU算法把最近沒有被使用的key放入到這個緩存中。
41 * Bitmap使用了SoftReference,當(dāng)內(nèi)存空間不足時(shí),此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方法中進(jìn)行網(wǎng)絡(luò)操作和文件操作,在onPostExecute方法中執(zhí)行回調(diào)函數(shù),把獲取的bitmap數(shù)據(jù)發(fā)送到UI線程與ListView中的imageView進(jìn)行關(guān)聯(lián),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 + "中下載圖片時(shí)出錯!,錯誤碼:" +
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 * 計(jì)算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 * 修改文件的最后修改時(shí)間
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 *計(jì)算存儲目錄下的文件大小,
212 * 當(dāng)文件總大小大于規(guī)定的CACHE_SIZE或者sdcard剩余空間小于FREE_SD_SPACE_NEEDED_TO_CACHE的規(guī)定
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 根據(jù)文件的最后修改時(shí)間進(jìn)行排序 *
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 }
?第五步、運(yùn)行工程之后,運(yùn)行效果如下圖示。
? ?? ???運(yùn)行工程,最開始時(shí),ListView使用默認(rèn)的圖片填充imageview。
? ?然后使用異步方式獲取網(wǎng)絡(luò)圖片,當(dāng)獲取到網(wǎng)絡(luò)圖片后,使用最新的網(wǎng)絡(luò)圖片替換默認(rèn)圖片。
?
?當(dāng)用戶拖動ListView,顯示效果如下圖所示:
?
?再次運(yùn)行工程后,加載圖片的速度會非常塊,因?yàn)槌绦蛟谑謾C(jī)內(nèi)存中存儲了相應(yīng)的圖片資源,直接加載這些資源就好了,不需要再訪問網(wǎng)絡(luò)。
? ?? ???下圖是在DDMS的File Explorer中的顯示:
?
轉(zhuǎn)載于:https://www.cnblogs.com/elefish/archive/2013/02/15/2912809.html
總結(jié)
以上是生活随笔為你收集整理的android使用软引用构建缓存的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。