网站机房建设方案,wordpress 首页缩略图,重庆app制作,制作好的网站必须申请如果每次加载同一张图片都要从网络获取#xff0c;那代价实在太大了。所以同一张图片只要从网络获取一次就够了#xff0c;然后在本地缓存起来#xff0c;之后加载同一张图片时就从缓存中加载就可以了。从内存缓存读取图片是最快的#xff0c;但是因为内存容量有限#xf…如果每次加载同一张图片都要从网络获取那代价实在太大了。所以同一张图片只要从网络获取一次就够了然后在本地缓存起来之后加载同一张图片时就从缓存中加载就可以了。从内存缓存读取图片是最快的但是因为内存容量有限所以最好再加上文件缓存。文件缓存空间也不是无限大的容量越大读取效率越低因此可以设置一个限定大小比如10M或者限定保存时间比如一天。因此加载图片的流程应该是1、先从内存缓存中获取取到则返回取不到则进行下一步2、从文件缓存中获取取到则返回并更新到内存缓存取不到则进行下一步3、从网络下载图片并更新到内存缓存和文件缓存。 后两个步骤纯碎属于业务逻辑暂且不表这里来看一下手Q使用的图片缓存管理策略。 说到缓存管理首先谈一下java中的强引用和弱引用
强引用最普遍的引用若一个对象具有强引用那么GC绝不会回收它。如A a new A()弱引用 弱引用又分为以下三类 软引用SoftReference: 这类引用只有当内存空间不足GC才会回收它弱引用WeakReference: 这类引用拥有更短的生命周期,GC扫描过程中一旦发现了此类引用不管当前内存是否足够立即回收虚引用PhantomRefence: 这类引用并不会决定对象的生命周期如果一个对象仅持有虚引用则任何时刻都可能被回收 下面来看看这样一个图片缓存类为了更大限度使用缓存它使用了强引用缓存强引用和弱引用缓存弱引用双重缓存强引用缓存不会轻易被回收用来保存常用数据不常用的转入弱引用缓存。**
ImageCache.javapublic class ImageCache {private static final String TAG ImageCache;//CustomLruCache是一个继承了LruCache的继承类它代表强引用缓存它的缓存大小一般由业务方提供private CustomLruCacheString, Drawable mMemoryCache;// Default memory cache size//这里设置的是弱引用缓存以及它所占据的空间大小private static final int DEFAULT_MEM_CACHE_SIZE 5; // 5MBprivate final HashMapString, WeakReferenceDrawable mRefCache new HashMapString, WeakReferenceDrawable();public ImageCache(int memSize) {memSize Math.max(memSize, DEFAULT_MEM_CACHE_SIZE);QLog.d(TAG, Memory cache size memSize MB);mMemoryCache new CustomLruCacheString, Drawable(memSize * 1024 * 1024) {//这里重写了LruCache的sizeOf方法来计算每个图片资源所占用内存的字节数Overrideprotected int sizeOf(String key, Drawable drawable) {if (drawable instanceof BitmapDrawable) {Bitmap bitmap ((BitmapDrawable) drawable).getBitmap();if (bitmap ! null) {//若是bitmap位图则直接计算它的大小return bitmap.getRowBytes() * bitmap.getHeight();}return 0;} else if (drawable instanceof AnimationDrawable) {//若是逐帧动画则首先获取它所有的帧数再计算总共的大小AnimationDrawable anim (AnimationDrawable) drawable;int count anim.getNumberOfFrames();int memSize 0;for (int i 0; i count; i) {Drawable dr anim.getFrame(i);if (dr instanceof BitmapDrawable) {Bitmap bitmap ((BitmapDrawable) dr).getBitmap();if (bitmap ! null) {memSize bitmap.getRowBytes() * bitmap.getHeight();}}}return memSize;}return 0;}};}//从缓存中获取图片public Drawable getImageFromMemCache(String key) {Drawable memDrawable null;if (mMemoryCache ! null) {//首先从强引用缓存中获取图片若找到的话把元素移动到CustomLruCache的最后面从而保证它在LRU算法中最后被删除//疑问其实LinkedHashMap本身就存在LRU的算法机制因此get的时候会自动移入到队列尾部memDrawable mMemoryCache.remove(key);if (memDrawable ! null) {memDrawable memDrawable.getConstantState().newDrawable();mMemoryCache.put(key, memDrawable);return memDrawable;}}//强引用缓存中没有找到开始在弱引用缓存中查找WeakReferenceDrawable ref mRefCache.get(key);if (ref ! null) {//若找到的话这里是否添加一步将其从弱引用缓存移入强引用缓存中比较好memDrawable ref.get();if (memDrawable null) {mRefCache.remove(key);}}return memDrawable;}//添加图片到缓存这里不理解为什么要向强引用缓存和弱引用缓存都要添加一份public void addImageToCache(String data, Drawable drawable) {// Add to memory cacheif (mMemoryCache ! null mMemoryCache.get(data) null) {mMemoryCache.put(data, drawable);mRefCache.put(data, new WeakReferenceDrawable(drawable));}}//从缓存中删除资源public void removeImageFromCache(String data) {if (mRefCache ! null) {mRefCache.remove(data);}if (mMemoryCache ! null) {mMemoryCache.remove(data);}}public Drawable getImageFromDiskCache(String pathName) {// TODO 暂不支持disk cachereturn null;}public void clearCaches() {// mDiskCache.clearCache();mMemoryCache.evictAll();mRefCache.clear();}
} 整个缓存策略是使用弱引用缓存和强引用缓存配合使用并结合LRUCache在尽可能地利用缓存的基础上也大大提高了缓存命中率。我个人觉得这个类有改进的地方比如当LRUCache在移除元素的时候默认是直接删除掉。这里更好的方式是重写LRUCache的entryRemoved方法使得强引用缓存满的时候会根据LRU算法将最近最久没有被使用的图片自动移入弱引用缓存如下mMemoryCache new CustomLruCacheString, Drawable(memSize * 1024 * 1024) {//这里重写了LruCache的sizeOf方法来计算每个图片资源所占用内存的字节数Overrideprotected int sizeOf(String key, Drawable drawable) {.........}当强引用缓存满时会自动调用这个方法这时候将移除的元素添加到弱引用缓存中Overrideprotected void entryRemoved(boolean evicted, String key, Drawable oldDrawable, Drawable newDrawable) {if (oldDawable ! null) {mRefCache.put(data, new WeakReferenceDrawable(oldDawable));}}}; 接下来看内存缓存类ImageMemoryCache
public class ImageMemoryCache {private static final int SOFT_CACHE_SIZE 15; //软引用缓存容量private static LruCache mLruCache; //硬引用缓存private static LinkedHashMap mSoftCache; //软引用缓存public ImageMemoryCache(Context context) {int memClass ((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();int cacheSize 1024 * 1024 * memClass / 4; //硬引用缓存容量为系统可用内存的1/4mLruCache new LruCache(cacheSize) {Overrideprotected int sizeOf(String key, Bitmap value) {if (value ! null)return value.getRowBytes() * value.getHeight();elsereturn 0;}Overrideprotected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {if (oldValue ! null)// 硬引用缓存容量满的时候会根据LRU算法把最近没有被使用的图片转入此软引用缓存mSoftCache.put(key, new SoftReference(oldValue));}};mSoftCache new LinkedHashMap(SOFT_CACHE_SIZE, 0.75f, true) {private static final long serialVersionUID 6040103833179403725L;Overrideprotected boolean removeEldestEntry(Entry eldest) {if (size() SOFT_CACHE_SIZE){ return true; } return false; }};}public Bitmap getBitmapFromCache(String url) {Bitmap bitmap;//先从硬引用缓存中获取synchronized (mLruCache) {bitmap mLruCache.get(url);if (bitmap ! null) {//如果找到的话把元素移到LinkedHashMap的最前面从而保证在LRU算法中是最后被删除mLruCache.remove(url);mLruCache.put(url, bitmap);return bitmap;}}//如果硬引用缓存中找不到到软引用缓存中找synchronized (mSoftCache) { SoftReference bitmapReference mSoftCache.get(url);if (bitmapReference ! null) {bitmap bitmapReference.get();if (bitmap ! null) {//将图片移回硬缓存mLruCache.put(url, bitmap);mSoftCache.remove(url);return bitmap;} else {mSoftCache.remove(url);}}}return null;} public void addBitmapToCache(String url, Bitmap bitmap) {if (bitmap ! null) {synchronized (mLruCache) {mLruCache.put(url, bitmap);}}}public void clearCache() {mSoftCache.clear();}
}
接下来看内存缓存类ImageMemoryCache
public class ImageFileCache {private static final String CACHDIR ImgCach;private static final String WHOLESALE_CONV .cach;private static final int MB 1024*1024;private static final int CACHE_SIZE 10;private static final int FREE_SD_SPACE_NEEDED_TO_CACHE 10;public ImageFileCache() {//清理文件缓存removeCache(getDirectory());}public Bitmap getImage(final String url) { final String path getDirectory() / convertUrlToFileName(url);File file new File(path);if (file.exists()) {Bitmap bmp BitmapFactory.decodeFile(path);if (bmp null) {file.delete();} else {updateFileTime(path);return bmp;}}return null;}public void saveBitmap(Bitmap bm, String url) {if (bm null) {return;}//判断sdcard上的空间if (FREE_SD_SPACE_NEEDED_TO_CACHE freeSpaceOnSd()) {//SD空间不足return;}String filename convertUrlToFileName(url);String dir getDirectory();File dirFile new File(dir);if (!dirFile.exists())dirFile.mkdirs();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();} catch (FileNotFoundException e) {Log.w(ImageFileCache, FileNotFoundException);} catch (IOException e) {Log.w(ImageFileCache, IOException);}} private boolean removeCache(String dirPath) {File dir new File(dirPath);File[] files dir.listFiles();if (files null) {return true;}if (!android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {return false;}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_NEEDED_TO_CACHE freeSpaceOnSd()) {int removeFactor (int) ((0.4 * files.length) 1);Arrays.sort(files, new FileLastModifSort());for (int i 0; i removeFactor; i) {if (files[i].getName().contains(WHOLESALE_CONV)) {files[i].delete();}}}if (freeSpaceOnSd() CACHE_SIZE) {return false;}return true;}public void updateFileTime(String path) {File file new File(path);long newModifiedTime System.currentTimeMillis();file.setLastModified(newModifiedTime);}private int freeSpaceOnSd() {StatFs stat new StatFs(Environment.getExternalStorageDirectory().getPath());double sdFreeMB ((double)stat.getAvailableBlocks() * (double) stat.getBlockSize()) / MB;return (int) sdFreeMB;} private String convertUrlToFileName(String url) {String[] strs url.split(/);return strs[strs.length - 1] WHOLESALE_CONV;}private String getDirectory() {String dir getSDPath() / CACHDIR;return dir;}private String getSDPath() {File sdDir null;boolean sdCardExist Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED); //判断sd卡是否存在if (sdCardExist) {sdDir Environment.getExternalStorageDirectory(); //获取根目录}if (sdDir ! null) {return sdDir.toString();} else {return ;}} private 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;}}}}从网络获取图片
public class ImageGetFromHttp {private static final String LOG_TAG ImageGetFromHttp;public static Bitmap downloadBitmap(String url) {final HttpClient client new DefaultHttpClient();final HttpGet getRequest new HttpGet(url);try {HttpResponse response client.execute(getRequest);final int statusCode response.getStatusLine().getStatusCode();if (statusCode ! HttpStatus.SC_OK) {Log.w(LOG_TAG, Error statusCode while retrieving bitmap from url);return null;}final HttpEntity entity response.getEntity();if (entity ! null) {InputStream inputStream null;try {inputStream entity.getContent();FilterInputStream fit new FlushedInputStream(inputStream);return BitmapFactory.decodeStream(fit);} finally {if (inputStream ! null) {inputStream.close();inputStream null;}entity.consumeContent();}}} catch (IOException e) {getRequest.abort();Log.w(LOG_TAG, I/O error while retrieving bitmap from url, e);} catch (IllegalStateException e) {getRequest.abort();Log.w(LOG_TAG, Incorrect URL: url);} catch (Exception e) {getRequest.abort();Log.w(LOG_TAG, Error while retrieving bitmap from url, e);} finally {client.getConnectionManager().shutdown();}return null;}static class FlushedInputStream extends FilterInputStream {public FlushedInputStream(InputStream inputStream) {super(inputStream);}Overridepublic long skip(long n) throws IOException {long totalBytesSkipped 0L;while (totalBytesSkipped n) {long bytesSkipped in.skip(n - totalBytesSkipped);if (bytesSkipped 0L) {int b read();if (b 0) {break; // we reached EOF} else {bytesSkipped 1; // we read one byte}}totalBytesSkipped bytesSkipped;}return totalBytesSkipped;}}
}
最后获取一张图片的流程就如下代码所示public Bitmap getBitmap(String url) {// 从内存缓存中获取图片Bitmap result memoryCache.getBitmapFromCache(url);if (result null) {// 文件缓存中获取result fileCache.getImage(url);if (result null) {// 从网络获取result ImageGetFromHttp.downloadBitmap(url);if (result ! null) {fileCache.saveBitmap(result, url);memoryCache.addBitmapToCache(url, result);}} else {// 添加到内存缓存memoryCache.addBitmapToCache(url, result);}}return result;
}