Android每次加载图片很浪费时间。所以设计了一个图片缓存技术来解决每次android手机加载图片的问题

 

内存的读取速度是最快的,然后是文件的读取速度,最后是网络资源的读取

 

既然内存的读取时间最快,我们好好利用内存资源。将内存再分两层缓存

强引用缓存不会轻易被回收,来保存常用数据,不常用的资源放入软引用缓存中。

对于硬引用和软引用的介绍:

⑴强引用(StrongReference)
    强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。

⑵软引用(SoftReference)

如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存

 1,对于强引用和软引用的使用,我们首先去强引用缓存中去找图片资源,当没有发现时,就去软引用缓存中。当强引用的超额时,将最后使用的资源放入软引用缓存中,使用到软引用的资源时 ,则将资源重新放回强引用缓存池中。

 2,内存缓存池中找不到,就去文件中查找,

3,再找不到就只好去网络上下载了,下载到以后,分别将资源放入文件缓存和内存缓存中


Android对于InputStream流有个小bug在慢速网络的情况下可能产生中断,可以考虑重写FilterInputStream处理skip方法来解决这个bug。 BitmapFactory类的decodeStream方法在网络超时或较慢的时候无法获取完整的数据,这里我 们通过继承FilterInputStream类的skip方法来强制实现flush流中的数据,主要原理就是检查是否到文件末端,告诉http类是否继续。

static class FlushedInputStream extends FilterInputStream {

        public FlushedInputStream(InputStream inputStream) {

            super(inputStream);

        }

                                                       

        @Override

        public 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 class MainActivity extends Activity {  
      
        private ImageMemoryCache memoryCache;  
        private ImageFileCache fileCache;  
        private ImageView imageView;  
        @Override  
        protected void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
            setContentView(R.layout.main);  
            memoryCache=new ImageMemoryCache(this);  
            fileCache=new ImageFileCache();  
            imageView=(ImageView) findViewById(R.id.img);  
            Bitmap b=getBitmap("http://f.hiphotos.baidu.com/album/w%3D2048/sign=7aa167f79f2f07085f052d00dd1cb999/472309f7905298228f794c7bd6ca7bcb0b46d4c4.jpg");  
            imageView.setImageBitmap(b);  
          
              
              
        }  
      
        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;  
        }  
      
    }




内存中读取

public class ImageMemoryCache {  
        /** 
         * 从内存读取数据速度是最快的,为了更大限度使用内存,这里使用了两层缓存。 
         * 硬引用缓存不会轻易被回收,用来保存常用数据,不常用的转入软引用缓存。 
         */  
        private static final int SOFT_CACHE_SIZE = 15;  //软引用缓存容量  
        private static LruCache<String, Bitmap> mLruCache;  //硬引用缓存  
        private static LinkedHashMap<String, SoftReference<Bitmap>> mSoftCache;  //软引用缓存  
                                                                                                
        public ImageMemoryCache(Context context) {  
            int memClass = ((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();  
            int cacheSize = 1024 * 1024 * memClass / 4;  //硬引用缓存容量,为系统可用内存的1/4  
            mLruCache = new LruCache<String, Bitmap>(cacheSize) {  
                @Override  
                protected int sizeOf(String key, Bitmap value) {  
                    if (value != null)  
                        return value.getRowBytes() * value.getHeight();  
                    else  
                        return 0;  
                }  
                                                                                                
                @Override  
                protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {  
                    if (oldValue != null)  
                        // 硬引用缓存容量满的时候,会根据LRU算法把最近没有被使用的图片转入此软引用缓存  
                        mSoftCache.put(key, new SoftReference<Bitmap>(oldValue));  
                }  
            };  
            mSoftCache = new LinkedHashMap<String, SoftReference<Bitmap>>(SOFT_CACHE_SIZE, 0.75f, true) {  
                private static final long serialVersionUID = 6040103833179403725L;  
                @Override  
                protected boolean removeEldestEntry(Entry<String, SoftReference<Bitmap>> 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<Bitmap> 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();  
        }  
    }




文件中获取

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");  
        }  
    }   
                                                                  
    /** 
     * 计算存储目录下的文件大小, 
     * 当文件总大小大于规定的CACHE_SIZE或者sdcard剩余空间小于FREE_SD_SPACE_NEEDED_TO_CACHE的规定 
     * 那么删除40%最近没有被使用的文件 
     */  
    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);  
    }  
                                                                  
    /** 计算sdcard上的剩余空间 **/  
    private int freeSpaceOnSd() {  
        StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath());  
        double sdFreeMB = ((double)stat.getAvailableBlocks() * (double) stat.getBlockSize()) / MB;  
        return (int) sdFreeMB;  
    }   
                                                                  
    /** 将url转成文件名 **/  
    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;  
    }  
                                                                  
    /** 取SD卡路径 **/  
    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<File> {  
        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;  
        }  
                                                             
        /* 
         * An InputStream that skips the exact number of bytes provided, unless it reaches EOF. 
         */  
        static class FlushedInputStream extends FilterInputStream {  
            public FlushedInputStream(InputStream inputStream) {  
                super(inputStream);  
            }  
                                                             
            @Override  
            public 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;  
            }  
        }  
    }



权限

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



引用

(这是双缓存绘图,和获取图片两回事)