使用软引用构建缓存(转载)
2012-12-21 16:29
417 查看
一、为什么要使用软引用
在上面关于软引用的介绍中,已经提到了软引用的特性。使用SoftReference引用的对象会有很长的生命周期,只有当系统的内存不足的时候,才会去释放这些软引用对象。所以可以使用软引用来缓存一些比较昂贵的资源,比如获取的网络图片数据。
当应用从网络中获取网络图片数据时,用户完全有可能做一些重复性的操作去查看相同的图片信息。对于这样的问题,通常会有两种解决方法:一种是把过去查看过的图片信息保存在内存中,每一个存储了图片信息的Java对象的生命周期都贯穿整个应用程序生命周期,另一种是当用户开始查看其他图片信息的时候,把存储了当前的图片信息的Java对象结束引用,使得垃圾收集线程可以回收其所占用的内存空间,当用户再次需要浏览该图片信息的时候,重新获取图片信息。
很显然,第一种实现方法将造成大量的内存浪费,而第二种实现的缺陷在于即使垃圾收集线程还没有进行垃圾收集,包含图片信息的对象仍然完好地保存在内存中,应用程序也要重新构建一个对象。
像访问磁盘文件、访问网络资源、查询数据库等操作都是影响应用程序执行性能的重要因素,如果能重新获取那些尚未被回收的Java对象的引用,必将减少不必要的访问,大大提高程序的运行速度。
这样看来,使用软引用是非常有必要的一件事情。
二、如何使用软引用
SoftReference的特点是它的一个实例保存着一个Java对象的软引用,该软引用的存在不妨碍垃圾收集器线程对该Java对象的回收。也就是说,一旦SoftReference保存着一个Java对象的软引用之后,在垃圾收集器线程对这个Java对象回收之前,SoftReference类所提供的get()方法都会返回这个Java对象的强引用。另外,一旦垃圾线程回收该Java对象之后,get()方法将返回null。
软引用的使用方法如下面的Java代码所示:
上面的代码执行后,对于MyObject对象,有两个引用路径,一个是来自aSoftRef对象的软引用,一个来自变量aRef的强引用,所以MyObject对象是强可及对象。紧跟着,可以使用下面的java的代码结束aReference对MyObject实例的强引用:
此后,MyObject对象成为了软可及对象。如果垃圾收集线程进行内存垃圾收集,并不会因为有一个SoftReference对该对象的引用而始终保留该对象。Java虚拟机的垃圾收集线程对软可及对象和其他一般Java对象进行了区别对待,软可及对象的清理是由垃圾收集线程根据其特定算法按照内存需求决定的。也就是说,垃圾收集线程会在虚拟机抛出OutOfMemoryError之前回收软可及对象,而且虚拟机会尽可能优先回收长时间闲置不用的软可及对象,对那些刚刚构建的或刚刚使用过的“新”软可及对象会被虚拟机尽可能保留。如果想获取软引用中包含的对象,可以使用下面的Java代码:
在回收这些对象之前,可以通过上面的代码重新获得对该实例的强引用。而回收之后,当调用软引用的get()方法时,返回的是null。
三、如何使用ReferenceQueue
作为一个Java对象,SoftReference对象除了具有保存软引用的特殊性之外,也具有Java对象的一般性。所以,当软可及对象被回收之后,虽然这个SoftReference对象的get()方法返回null,但这个SoftReference对象已经不再具有存在的价值,需要一个适当的清除机制,避免大量SoftReference对象带来的内存泄漏。在java.lang.ref包里还提供了ReferenceQueue。如果在创建SoftReference对象的时候,使用了带有一个ReferenceQueue对象作为参数的构造方法,如下面的Java代码:
当这个SoftReference所软引用的aMyOhject被垃圾收集器回收的同时,ref所强引用的SoftReference对象被列入ReferenceQueue。也就是说,ReferenceQueue中保存的对象是Reference对象,而且是已经失去了它所软引用的对象的Reference对象。另外从ReferenceQueue这个名字也可以看出,它是一个队列,当调用它的poll()方法的时候,如果这个队列中不是空队列,那么将返回队列前面的那个Reference对象。
在任何时候,都可以调用ReferenceQueue的poll()方法来检查是否有它所关心的非强可及对象被回收。如果队列为空,将返回一个null,否则该方法返回队列中最前面一个Reference对象。利用这个方法,可以检查哪个SoftReference所软引用的对象已经被回收。可以把这些失去所软引用的对象的SoftReference对象清除掉,如下面的Java代码所示。:
四、实例分析
理解了Java中的引用机制之后就可以在Android中构造缓存器(cache)了,在Android中应用比较多的控件是ListView,通常会使用ListView显示网络数据列表,同时会包含图片缩略图,当数据量很大的时候,为了让用户能更流畅地流量信息,可以使用异步加载和缓存机制处理网络图片。
通过以上对于Java软引用类型的了解,可以知道使用软引用来构建缓存是比较合适的。虽然软引用能够延长数据对象的生命周期,但是对于移动设备来说,内存资源相对来说比较紧缺,仅使用软引用未必能达到最佳的缓存效果。通常会使用一些组合方式来进行数据缓存,最常用的是强引用、软引用加本地缓存的方式。
Android提供了一个AsyncTask类,它封装了基本的异步操作模型,只需要实现几个最基本的方法就可以很容易的实现异步加载图片,主要的方法是doInBackground方法和onPostExecute方法。AsyncTask类会启动一个新的线程执行doInBackground方法,所以我们所有的网络操作都应该在这个方法中实现,当doInBackground方法执行完成后,AsyncTask类会使用内置的Handler发送消息在主线程中执行onPostExecute方法,所以关于对UI的操作都应该放在onPostExecute方法中实现。
对于缓存的处理,主要思路是:在开始时,创建两个缓存区域:强引用缓存区域和软引用缓存区域。在强引用缓存区中保存有限的图片对象,根据LRU策略把一些最不常用的图片对象移到软引用缓存区,当缓存区域中都没有图片对象时从网络加载图片。完成后把图片数据保存到SDCard中,并根据LRU策略进行管理SDCard中保存的图片文件。
下面通过一个ListView的使用实例来说明如何在Android应用程序中使用异步加载图片,并且在内存和本地缓存它们。
第一步,首先建立一个Android工程,名称为AsyncListImage,由于应用需要访问网络所以需要修改AndroidManifest.xml文件,添加网络连接的权限,代码如下:
第二步,修改main.xml文件添加listview控件,并设置listview的一些基本属性信息,如下面的xml代码:
第三步,修改AsyncListImageActivity类并覆盖oncreate方法,初始化listview,并创建listview控件使用的Adapter。在AsyncListImage中定义了两种缓存区域A和B,A代表强引用缓存区域,B代表软引用缓存区域,由于使用强引用缓存区域保存数据只能保存一定的数量,而不能一直往里面存放,需要设置数据的过期时间、LRU等算法。这里有一个方法是把常用的数据放到缓存A中,不常用的放到另外一个缓存B中。当要获取数据时先从A中去获取,如果A中不存在那么再去B中获取。B中的数据主要是A中经过LRU生成的数据,这里的内存回收主要针对B内存,从而保持A中的数据可以有效的被命中。
下面是完整的Java代码:
第四步,定义AsyncTask类的子类ImageDownloaderTask类并覆盖doInBackground方法和onPostExecute方法。在doInBackground方法中进行网络操作和文件操作,在onPostExecute方法中执行回调函数,把获取的bitmap数据发送到UI线程与ListView中的imageView进行关联,Java代码如下。
在上面关于软引用的介绍中,已经提到了软引用的特性。使用SoftReference引用的对象会有很长的生命周期,只有当系统的内存不足的时候,才会去释放这些软引用对象。所以可以使用软引用来缓存一些比较昂贵的资源,比如获取的网络图片数据。
当应用从网络中获取网络图片数据时,用户完全有可能做一些重复性的操作去查看相同的图片信息。对于这样的问题,通常会有两种解决方法:一种是把过去查看过的图片信息保存在内存中,每一个存储了图片信息的Java对象的生命周期都贯穿整个应用程序生命周期,另一种是当用户开始查看其他图片信息的时候,把存储了当前的图片信息的Java对象结束引用,使得垃圾收集线程可以回收其所占用的内存空间,当用户再次需要浏览该图片信息的时候,重新获取图片信息。
很显然,第一种实现方法将造成大量的内存浪费,而第二种实现的缺陷在于即使垃圾收集线程还没有进行垃圾收集,包含图片信息的对象仍然完好地保存在内存中,应用程序也要重新构建一个对象。
像访问磁盘文件、访问网络资源、查询数据库等操作都是影响应用程序执行性能的重要因素,如果能重新获取那些尚未被回收的Java对象的引用,必将减少不必要的访问,大大提高程序的运行速度。
这样看来,使用软引用是非常有必要的一件事情。
二、如何使用软引用
SoftReference的特点是它的一个实例保存着一个Java对象的软引用,该软引用的存在不妨碍垃圾收集器线程对该Java对象的回收。也就是说,一旦SoftReference保存着一个Java对象的软引用之后,在垃圾收集器线程对这个Java对象回收之前,SoftReference类所提供的get()方法都会返回这个Java对象的强引用。另外,一旦垃圾线程回收该Java对象之后,get()方法将返回null。
软引用的使用方法如下面的Java代码所示:
1 | MyObjectaRef= new MyObject(); //创建一个对象 |
2 | SoftReferenceaSoftRef= new SoftReference(aRef); //创建对象的软引用 |
1 | aRef= null ; //断开对象的强引用 |
1 | MyObjectanotherRef=(MyObject)aSoftRef.get(); //通过软引用获取对象 |
三、如何使用ReferenceQueue
作为一个Java对象,SoftReference对象除了具有保存软引用的特殊性之外,也具有Java对象的一般性。所以,当软可及对象被回收之后,虽然这个SoftReference对象的get()方法返回null,但这个SoftReference对象已经不再具有存在的价值,需要一个适当的清除机制,避免大量SoftReference对象带来的内存泄漏。在java.lang.ref包里还提供了ReferenceQueue。如果在创建SoftReference对象的时候,使用了带有一个ReferenceQueue对象作为参数的构造方法,如下面的Java代码:
1 | ReferenceQueuequeue= new ReferenceQueue(); //创建引用队列 |
2 | SoftReferenceref= new SoftReference(aMyObject,queue); //把引用加入到引用队列 |
在任何时候,都可以调用ReferenceQueue的poll()方法来检查是否有它所关心的非强可及对象被回收。如果队列为空,将返回一个null,否则该方法返回队列中最前面一个Reference对象。利用这个方法,可以检查哪个SoftReference所软引用的对象已经被回收。可以把这些失去所软引用的对象的SoftReference对象清除掉,如下面的Java代码所示。:
1 | SoftReferenceref= 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的使用实例来说明如何在Android应用程序中使用异步加载图片,并且在内存和本地缓存它们。
第一步,首先建立一个Android工程,名称为AsyncListImage,由于应用需要访问网络所以需要修改AndroidManifest.xml文件,添加网络连接的权限,代码如下:
1 | < uses-permission android:name = "android.permission.INTERNET" /> |
01 | <? xml version = "1.0" encoding = "utf-8" ?> |
02 | < LinearLayout xmlns:android = " |
03 | android:orientation = "vertical" |
04 | android:layout_width = "fill_parent" |
05 | android:layout_height = "fill_parent" > |
06 | < ListView android:id = "@+id/list" |
07 | android:layout_width = "fill_parent" |
08 | android:layout_height = "fill_parent" |
09 | android:background = "#ffffffff" |
10 | android:cacheColorHint = "#00000000" /> |
11 | </ LinearLayout > |
下面是完整的Java代码:
001 | package com.devdiv.android.asynimagelist; |
002 |
003 | import java.io.File; |
004 | import java.lang.ref.SoftReference; |
005 | import java.util.HashMap; |
006 | import java.util.LinkedHashMap; |
007 | import java.util.concurrent.ConcurrentHashMap; |
008 |
009 | import android.app.Activity; |
010 | import android.graphics.Bitmap; |
011 | import android.os.Bundle; |
012 | import android.util.Log; |
013 | import android.view.View; |
014 | import android.view.ViewGroup; |
015 | import android.widget.BaseAdapter; |
016 | import android.widget.ImageView; |
017 | import android.widget.ListView; |
018 | import android.widget.ImageView.ScaleType; |
019 |
020 | @SuppressWarnings ( "serial" ) |
021 | public class AsyncListImage extends Activity implements RemoteImageCallback{ |
022 | private ListViewlist; |
023 | private static final StringTAG=AsyncListImage. class .getSimpleName(); |
024 | private static final int HARD_CACHE_CAPACITY= 10 ; |
025 |
026 | private final HashMap<String,Bitmap>mHardBitmapCache= new LinkedHashMap<String,Bitmap>(HARD_CACHE_CAPACITY/ 2 , 0 .75f, true ){ |
027 |
028 | @Override |
029 | protected boolean removeEldestEntry(LinkedHashMap.Entry<String,Bitmap>eldest){ |
030 | if (size()>HARD_CACHE_CAPACITY){ |
031 | //当map的size大于10时,把最近不常用的key放到mSoftBitmapCache中,从而保证mHardBitmapCache的效率 |
032 | mSoftBitmapCache.put(eldest.getKey(), new SoftReference<Bitmap>(eldest.getValue())); |
033 | return true ; |
034 | } else |
035 | return false ; |
036 | } |
037 | }; |
038 |
039 | /** |
040 | *当mHardBitmapCache的key大于10的时候,会根据LRU算法把最近没有被使用的key放入到这个缓存中。 |
041 | *Bitmap使用了SoftReference,当内存空间不足时,此cache中的bitmap会被垃圾回收掉 |
042 | */ |
043 | private final static ConcurrentHashMap<String,SoftReference<Bitmap>>mSoftBitmapCache= new ConcurrentHashMap<String,SoftReference<Bitmap>>( |
044 | HARD_CACHE_CAPACITY/ 2 ); |
045 |
046 | @Override |
047 | public void onCreate(BundlesavedInstanceState){ |
048 | super .onCreate(savedInstanceState); |
049 | setContentView(R.layout.main); |
050 | list=(ListView)findViewById(R.id.list); |
051 |
052 | initCacheDir(); |
053 |
054 | MyListAdapteradapter= new MyListAdapter(); |
055 | list.setAdapter(adapter); |
056 | } |
057 |
058 | private void initCacheDir(){ |
059 | StringcacheDir= "/data/data/com.devdiv.android.asynimagelist/files/caches" ; |
060 | Filef= new File(cacheDir); |
061 | if (!f.exists()){ |
062 | f.mkdirs(); |
063 | } |
064 | } |
065 |
066 | private class MyListAdapter extends BaseAdapter{ |
067 | private String[]urls= new String[]{ |
068 | " , |
069 | " , |
070 | " , |
071 | " , |
072 | " , |
073 | " , |
074 | " , |
075 | " , |
076 | " , |
077 | " , |
078 | " , |
079 | " |
080 | }; |
081 |
082 | @Override |
083 | public int getCount(){ |
084 | return urls.length; |
085 | } |
086 |
087 | @Override |
088 | public StringgetItem( int position){ |
089 | return urls[position]; |
090 | } |
091 |
092 | @Override |
093 | public long getItemId( int position){ |
094 | return position; |
095 | } |
096 |
097 | @Override |
098 | public ViewgetView( int position,ViewconvertView,ViewGroupparent){ |
099 | if (convertView== null ){ |
100 | convertView= new ImageView(AsyncListImage. this ); |
101 | } |
102 | ImageViewiv=(ImageView)convertView; |
103 | iv.setScaleType(ScaleType.FIT_START); |
104 | Bitmapbitmap=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 BitmapgetBitmapFromCache(Stringurl){ |
122 | //先从mHardBitmapCache缓存中获取 |
123 | synchronized (mHardBitmapCache){ |
124 | final Bitmapbitmap=mHardBitmapCache.get(url); |
125 | if (bitmap!= null ){ |
126 | //如果找到的话,把元素移到linkedhashmap的最前面,从而保证在LRU算法中是最后被删除 |
127 | mHardBitmapCache.remove(url); |
128 | Log.d(TAG, "movebitmaptotheheadoflinkedhashmap:" +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 Bitmapbitmap=bitmapReference.get(); |
137 | if (bitmap!= null ){ |
138 | Log.d(TAG, "getbitmapfrommSoftBitmapCachewithkey:" +url); |
139 | return bitmap; |
140 | } else { |
141 | mSoftBitmapCache.remove(url); |
142 | Log.d(TAG, "removebitmapwithkey:" +url); |
143 | } |
144 | } |
145 | return null ; |
146 | } |
147 |
148 | @Override |
149 | public void onComplete(Stringurl,Bitmapbitmap){ |
150 | Log.d(TAG, "onCompleteaftergotbitmapfromremotewithkey:" +url); |
151 | ImageViewiv=(ImageView)list.findViewWithTag(url); |
152 | if (iv!= null ){ |
153 | iv.setImageBitmap(bitmap); |
154 | mHardBitmapCache.put(url,bitmap); |
155 | } |
156 | } |
157 |
158 | } |
001 | package com.devdiv.android.asynimagelist; |
002 |
003 | import java.io.BufferedOutputStream; |
004 | import java.io.ByteArrayOutputStream; |
005 | import java.io.Closeable; |
006 | import java.io.File; |
007 | import java.io.FileNotFoundException; |
008 | import java.io.FileOutputStream; |
009 | import java.io.IOException; |
010 | import java.io.InputStream; |
011 | import java.io.OutputStream; |
012 | import java.lang.ref.WeakReference; |
013 | import java.util.Arrays; |
014 | import java.util.Comparator; |
015 |
016 | import org.apache.http.HttpEntity; |
017 | import org.apache.http.HttpResponse; |
018 | import org.apache.http.HttpStatus; |
019 | import org.apache.http.client.methods.HttpGet; |
020 | import org.apache.http.impl.client.DefaultHttpClient; |
021 |
022 | import android.graphics.Bitmap; |
023 | import android.graphics.BitmapFactory; |
024 | import android.os.AsyncTask; |
025 | import android.os.Environment; |
026 | import android.os.StatFs; |
027 | import android.util.Log; |
028 |
029 | public class ImageDownloaderTask extends AsyncTask<String,Void,Bitmap>{ |
030 | private static StringTAG=ImageDownloaderTask. class .getSimpleName(); |
031 | private static final int IO_BUFFER_SIZE= 4 * 1024 ; |
032 | private static final int MB= 1024 * 1024 ; |
033 | private static final int CACHE_SIZE= 1024 * 1024 ; |
034 | private static final int mTimeDiff= 5 * 24 * 60 * 60 * 1000 ; |
035 | private static final int FREE_SD_SPACE_NEEDED_TO_CACHE= 30 ; |
036 | private static final StringWHOLESALE_CONV= "/data/data/com.devdiv.android.asynimagelist/files/caches" ; |
037 | private Stringurl; |
038 | private final WeakReference<AsyncListImage>activityReference; |
039 |
040 | public ImageDownloaderTask(AsyncListImageactivity){ |
041 | activityReference= new WeakReference<AsyncListImage>(activity); |
042 | } |
043 |
044 | @Override |
045 | protected BitmapdoInBackground(String...params){ |
046 | url=params[ 0 ]; |
047 | Stringfilename=convertUrlToFileName(url); |
048 | Stringdir=getDirectory(filename); |
049 | Filefile= new File(dir+ "/" +filename); |
050 | if (file.exists()){ |
051 | removeExpiredCache(dir,filename); |
052 | updateFileTime(dir,filename); |
053 | Bitmapbitmap=BitmapFactory.decodeFile(file.getAbsolutePath()); |
054 | if (bitmap!= null ) |
055 | return bitmap; |
056 | } |
057 |
058 | final DefaultHttpClientclient= new DefaultHttpClient(); |
059 |
060 | final HttpGetgetRequest= new HttpGet(url); |
061 | try { |
062 | HttpResponseresponse=client.execute(getRequest); |
063 | final int statusCode=response.getStatusLine().getStatusCode(); |
064 | if (statusCode!=HttpStatus.SC_OK){ |
065 | Log.w(TAG, "从" +url+ "中下载图片时出错!,错误码:" +statusCode); |
066 | return null ; |
067 | } |
068 | final HttpEntityentity=response.getEntity(); |
069 | if (entity!= null ){ |
070 | InputStreaminputStream= null ; |
071 | OutputStreamoutputStream= null ; |
072 | try { |
073 | inputStream=entity.getContent(); |
074 | final ByteArrayOutputStreamdataStream= new ByteArrayOutputStream(); |
075 | outputStream= new BufferedOutputStream(dataStream, |
076 | IO_BUFFER_SIZE); |
077 | copy(inputStream,outputStream); |
078 | outputStream.flush(); |
079 | final byte []data=dataStream.toByteArray(); |
080 | final Bitmapbitmap=BitmapFactory.decodeByteArray(data, |
081 | 0 ,data.length); |
082 |
083 | saveBmpToSd(bitmap,url); |
084 |
085 | return bitmap; |
086 | } finally { |
087 | closeStream(inputStream); |
088 | closeStream(outputStream); |
089 | entity.consumeContent(); |
090 | } |
091 | } |
092 | } catch (IOExceptione){ |
093 | getRequest.abort(); |
094 | Log.w(TAG, "I/Oerrorwhileretrievingbitmapfrom" +url,e); |
095 | } catch (IllegalStateExceptione){ |
096 | getRequest.abort(); |
097 | Log.w(TAG, "IncorrectURL:" +url); |
098 | } catch (Exceptione){ |
099 | getRequest.abort(); |
100 | Log.w(TAG, "Errorwhileretrievingbitmapfrom" +url,e); |
101 | } |
102 | return null ; |
103 | } |
104 |
105 | @Override |
106 | protected void onPostExecute(Bitmapresult){ |
107 | super .onPostExecute(result); |
108 | AsyncListImageact=activityReference.get(); |
109 | if (act!= null &&result!= null ){ |
110 | act.onComplete(url,result); |
111 | } |
112 | } |
113 |
114 |
115 | /** |
116 | *Copythecontentoftheinputstreamintotheoutputstream,usingatemporary |
117 | *bytearraybufferwhosesizeisdefinedby{@link#IO_BUFFER_SIZE}. |
118 | * |
119 | *@paraminTheinputstreamtocopyfrom. |
120 | *@paramoutTheoutputstreamtocopyto. |
121 | * |
122 | *@throwsjava.io.IOExceptionIfanyerroroccursduringthecopy. |
123 | */ |
124 | public static void copy(InputStreamin,OutputStreamout) 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 | *Closesthespecifiedstream. |
134 | * |
135 | *@paramstreamThestreamtoclose. |
136 | */ |
137 | public static void closeStream(Closeablestream){ |
138 | if (stream!= null ){ |
139 | try { |
140 | stream.close(); |
141 | } catch (IOExceptione){ |
142 | android.util.Log.e(TAG, "Couldnotclosestream" ,e); |
143 | } |
144 | } |
145 | } |
146 |
147 | private void saveBmpToSd(Bitmapbm,Stringurl){ |
148 | if (bm== null ){ |
149 | Log.w(TAG, "tryingtosavenullbitmap" ); |
150 | return ; |
151 | } |
152 | //判断sdcard上的空间 |
153 | if (FREE_SD_SPACE_NEEDED_TO_CACHE>freeSpaceOnSd()){ |
154 | Log.w(TAG, "Lowfreespaceonsd,donotcache" ); |
155 | removeCache(WHOLESALE_CONV); |
156 | return ; |
157 | } |
158 | Stringfilename=convertUrlToFileName(url); |
159 | Stringdir=getDirectory(filename); |
160 | Filefile= new File(dir+ "/" +filename); |
161 | try { |
162 | file.createNewFile(); |
163 | OutputStreamoutStream= new FileOutputStream(file); |
164 | bm.compress(Bitmap.CompressFormat.JPEG, 100 ,outStream); |
165 | outStream.flush(); |
166 | outStream.close(); |
167 | Log.i(TAG, "Imagesavedtosd" ); |
168 | } catch (FileNotFoundExceptione){ |
169 | Log.w(TAG, "FileNotFoundException" ); |
170 | } catch (IOExceptione){ |
171 | Log.w(TAG, "IOException" ); |
172 | } |
173 | } |
174 |
175 | private StringconvertUrlToFileName(Stringurl){ |
176 | int lastIndex=url.lastIndexOf( '/' ); |
177 | return url.substring(lastIndex+ 1 ); |
178 | } |
179 |
180 | private StringgetDirectory(Stringfilename){ |
181 | return WHOLESALE_CONV; |
182 | } |
183 |
184 | /** |
185 | *计算sdcard上的剩余空间 |
186 | * |
187 | *@return |
188 | */ |
189 | private int freeSpaceOnSd(){ |
190 | StatFsstat= 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 | *@paramdir |
202 | *@paramfileName |
203 | */ |
204 | private void updateFileTime(Stringdir,StringfileName){ |
205 | Filefile= 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 | *@paramdirPath |
216 | *@paramfilename |
217 | */ |
218 | private void removeCache(StringdirPath){ |
219 | Filedir= 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, "Clearsomeexpiredcachefiles" ); |
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(Filearg0,Filearg1){ |
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 | *@paramdirPath |
271 | *@paramfilename |
272 | */ |
273 | private void removeExpiredCache(StringdirPath,Stringfilename){ |
274 |
275 | Filefile= new File(dirPath,filename); |
276 |
277 | if (System.currentTimeMillis()-file.lastModified()>mTimeDiff){ |
278 |
279 | Log.i(TAG, "Clearsomeexpiredcachefiles" ); |
280 |
281 | file.delete(); |
282 |
283 | } |
284 |
285 | } |
286 | } |
相关文章推荐
- 代码片段三:使用软引用构建敏感数据的缓存
- android使用软引用构建缓存
- 使用软引用构建缓存
- android使用软引用构建缓存
- 使用软引用构建缓存
- 使用软引用构建敏感数据的缓存
- Java系列:使用软引用构建敏感数据的缓存
- 使用memc-nginx和srcache-nginx模块构建高效透明的缓存机制
- 【转载】在Windows下Memcache缓存系统构建(Java)
- 使用angularjs1.x构建前台开发框架(十)——一个完整第三方组件的引用
- 使用软引用缓存Bitmap
- OVS初级教程:使用open vswitch构建虚拟网络(转载)
- [转载] 构建微服务:使用API Gateway
- Android开发优化之——使用软引用和弱引用(转载)
- 利用软引用和引用队列构建软引用缓存
- 使用webstorm+webpack构建简单入门级“HelloWorld”的应用&&引用jquery来实现alert
- 【转】使用缓存构建更快的 Web 应用程序
- 使用软引用缓存Bitmap
- [转] 使用memc-nginx和srcache-nginx模块构建高效透明的缓存机制
- 如何使用浏览器缓存来加快站点的访问速度 转载