图片加载避免OOM+代码示例

来源:互联网 发布:网络编辑的工资 编辑:程序博客网 时间:2024/06/04 01:36

总结学习笔记!
一、Android四大图片缓存框架

Android四大图片缓存(Imageloader,Picasso,Glide,Fresco)原理、特性对比

二、OOM问题剖析和解决方案

Android应用中OOM问题剖析和解决方案

三、避免OOM加载图片代码示例
1、ImageLoader
三级的缓存结构:内存绑定url和bitmap,内存绑定url和imageview,内存绑定url和file
先再内存中取,得不到了去读取缓存文件,仍然得不到去请求网络下载,下载到缓存中,然后绑定到内存中
DisplayImage(url,ImageView );调用入口

public class ImageLoader {    MemoryCache memoryCache=new MemoryCache();    FileCache fileCache;    private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());    ExecutorService executorService;    Handler handler=new Handler();//handler to display images in UI thread    public ImageLoader(Context context){        fileCache=new FileCache(context);        executorService=Executors.newFixedThreadPool(5);    }    final int stub_id=R.drawable.stub;    public void DisplayImage(String url, ImageView imageView)    {        //每个条目对应一个url        imageViews.put(imageView, url);        //先从内存中拿到url对应的bitmap        Bitmap bitmap=memoryCache.get(url);        if(bitmap!=null)            //内存拿到立即渲染            imageView.setImageBitmap(bitmap);        else        {            //去走缓存和网络            queuePhoto(url, imageView);            imageView.setImageResource(stub_id);        }    }    private void queuePhoto(String url, ImageView imageView)    {        //主要是为了防止错位        PhotoToLoad p=new PhotoToLoad(url, imageView);        //线程池执行加载        executorService.submit(new PhotosLoader(p));    }    private Bitmap getBitmap(String url)     {        File f=fileCache.getFile(url);        //from SD cache        Bitmap b = decodeFile(f);        if(b!=null)            return b;        //from web        try {            Bitmap bitmap=null;            URL imageUrl = new URL(url);            HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();            conn.setConnectTimeout(30000);            conn.setReadTimeout(30000);            conn.setInstanceFollowRedirects(true);            InputStream is=conn.getInputStream();            OutputStream os = new FileOutputStream(f);            Utils.CopyStream(is, os);            os.close();            conn.disconnect();            bitmap = decodeFile(f);            return bitmap;        } catch (Throwable ex){           ex.printStackTrace();           if(ex instanceof OutOfMemoryError)               memoryCache.clear();           return null;        }    }    //decodes image and scales it to reduce memory consumption    //控制加载进入的大小,进行图片压缩,防止单张图片过大造成oom并且节省内存,还能拿到需要的大小的图片    private Bitmap decodeFile(File f){        try {            //decode image size            BitmapFactory.Options o = new BitmapFactory.Options();            o.inJustDecodeBounds = true;            FileInputStream stream1=new FileInputStream(f);            BitmapFactory.decodeStream(stream1,null,o);            stream1.close();            //Find the correct scale value. It should be the power of 2.            final int REQUIRED_SIZE=70;            int width_tmp=o.outWidth, height_tmp=o.outHeight;            int scale=1;            while(true){                if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)                    break;                width_tmp/=2;                height_tmp/=2;                scale*=2;            }            //decode with inSampleSize            BitmapFactory.Options o2 = new BitmapFactory.Options();            o2.inSampleSize=scale;            FileInputStream stream2=new FileInputStream(f);            Bitmap bitmap=BitmapFactory.decodeStream(stream2, null, o2);            stream2.close();            return bitmap;        } catch (FileNotFoundException e) {        }         catch (IOException e) {            e.printStackTrace();        }        return null;    }    //Task for the queue    private class PhotoToLoad    {        public String url;        public ImageView imageView;        public PhotoToLoad(String u, ImageView i){            url=u;             imageView=i;        }    }    class PhotosLoader implements Runnable {        PhotoToLoad photoToLoad;        PhotosLoader(PhotoToLoad photoToLoad){            this.photoToLoad=photoToLoad;        }        @Override        public void run() {            try{                if(imageViewReused(photoToLoad))                    return;                    //网络下载到本地缓存                Bitmap bmp=getBitmap(photoToLoad.url);                    /绑定到内存                memoryCache.put(photoToLoad.url, bmp);                if(imageViewReused(photoToLoad))                    return;                BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad);                handler.post(bd);            }catch(Throwable th){                th.printStackTrace();            }        }    }    boolean imageViewReused(PhotoToLoad photoToLoad){        String tag=imageViews.get(photoToLoad.imageView);        if(tag==null || !tag.equals(photoToLoad.url))            return true;        return false;    }    //Used to display bitmap in the UI thread    class BitmapDisplayer implements Runnable    {        Bitmap bitmap;        PhotoToLoad photoToLoad;        public BitmapDisplayer(Bitmap b, PhotoToLoad p){bitmap=b;photoToLoad=p;}        public void run()        {            if(imageViewReused(photoToLoad))                return;            if(bitmap!=null)                photoToLoad.imageView.setImageBitmap(bitmap);            else                photoToLoad.imageView.setImageResource(stub_id);        }    }    public void clearCache() {        memoryCache.clear();        fileCache.clear();    }}

2、MemoryCache
LinkedHashMap有lru的特性。
MemoryCache设置了内存限制,为当前应用运行内存的25%。
关注MemoryCache get() put()方法

public class MemoryCache {    private static final String TAG = "MemoryCache";    private Map<String, Bitmap> cache=Collections.synchronizedMap(            new LinkedHashMap<String, Bitmap>(10,1.5f,true));//Last argument true for LRU ordering    private long size=0;//current allocated size    private long limit=1000000;//max memory in bytes    public MemoryCache(){        //use 25% of available heap size        setLimit(Runtime.getRuntime().maxMemory()/4);    }    public void setLimit(long new_limit){        limit=new_limit;        Log.i(TAG, "MemoryCache will use up to "+limit/1024./1024.+"MB");    }    public Bitmap get(String id){        try{            if(!cache.containsKey(id))                return null;            //NullPointerException sometimes happen here http://code.google.com/p/osmdroid/issues/detail?id=78             return cache.get(id);        }catch(NullPointerException ex){            ex.printStackTrace();            return null;        }    }    public void put(String id, Bitmap bitmap){        try{            if(cache.containsKey(id))                size-=getSizeInBytes(cache.get(id));            cache.put(id, bitmap);            size+=getSizeInBytes(bitmap);            //检查加载进来新的bitmap后内存情况,准备清理内存            checkSize();        }catch(Throwable th){            th.printStackTrace();        }    }    private void checkSize() {        Log.i(TAG, "cache size="+size+" length="+cache.size());        if(size>limit){//加载进来后大于限制,就开始清理内存            Iterator<Entry<String, Bitmap>> iter=cache.entrySet().iterator();//least recently accessed item will be the first one iterated              while(iter.hasNext()){                Entry<String, Bitmap> entry=iter.next();                size-=getSizeInBytes(entry.getValue());                iter.remove();                if(size<=limit)//直到达到限制内                    break;            }            Log.i(TAG, "Clean cache. New size "+cache.size());        }    }    public void clear() {        try{            //NullPointerException sometimes happen here http://code.google.com/p/osmdroid/issues/detail?id=78             cache.clear();            size=0;        }catch(NullPointerException ex){            ex.printStackTrace();        }    }    long getSizeInBytes(Bitmap bitmap) {        if(bitmap==null)            return 0;        return bitmap.getRowBytes() * bitmap.getHeight();    }}

3.FileCache

public class FileCache {    private File cacheDir;    public FileCache(Context context){        //Find the dir to save cached images        if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))            cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"LazyList");        else            cacheDir=context.getCacheDir();        if(!cacheDir.exists())            cacheDir.mkdirs();    }    public File getFile(String url){        //I identify images by hashcode. Not a perfect solution, good for the demo.        String filename=String.valueOf(url.hashCode());        //Another possible solution (thanks to grantland)        //String filename = URLEncoder.encode(url);        File f = new File(cacheDir, filename);        return f;    }    public void clear(){        File[] files=cacheDir.listFiles();        if(files==null)            return;        for(File f:files)            f.delete();    }}

四、图片的压缩式必须的

因为图片的大小=长 X 宽 X 每个像素点占用的字节大小
所以压缩图片就从以上几个方面入手

[bitmap的六种压缩方式,Android图片压缩]
(http://blog.csdn.net/harryweasley/article/details/51955467)

最详细的Android图片压缩解释

原创粉丝点击