zoukankan      html  css  js  c++  java
  • android 网络图片双缓存

    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" />
  • 相关阅读:
    There are multiple modules with names that only differ in casing. 问题的一种解决方法
    微软官方tfs源码转移至华为软开云操作过程 by vs2019
    【转载】docker hub下载速度太慢,更新国内源
    visual studio 2019不能在vue文件中直接识别less语法
    visual studio 2019 中初始化 vue.js 项目
    vs在微软官方tfs创建私有项目过程
    WinCC OA-C#-开发一个EricManager
    WinCC OA-JS-CTRL脚本与JS交互
    WinCC OA-JS-使用plotly.js开发个性化图表
    WinCC OA-JS-WebView解析及开发环境搭建
  • 原文地址:https://www.cnblogs.com/dongdong230/p/4183127.html
Copyright © 2011-2022 走看看