关于android 图片加载优化

原文地址:http://blog.csdn.net/yudajun/article/details/9323941

android应用对图片处理算是比较频繁的了,尤其是在程序加载大量图片和高分辨率图片时,最容易产生oom异常,下面是个人平时一些省内存加载方法

方法一:

    public Bitmap decodeFile(String filePath) {  
            Bitmap bitmap = null;  
            BitmapFactory.Options options = new BitmapFactory.Options();  
            options.inPurgeable = true;  
            try {  
                BitmapFactory.Options.class.getField("inNativeAlloc").setBoolean(  
                        options, true);  
            } catch (IllegalArgumentException e) {  
                e.printStackTrace();  
            } catch (SecurityException e) {  
                e.printStackTrace();  
            } catch (IllegalAccessException e) {  
                e.printStackTrace();  
            } catch (NoSuchFieldException e) {  
                e.printStackTrace();  
            }  
            if (mFilePath != null) {  
                bitmap = BitmapFactory.decodeFile(mFilePath, options);  
            }  
            return bitmap;  
        }  

方法二:

public Bitmap ReadBitMap(Context context, int resId){      
  
            BitmapFactory.Options opt = new BitmapFactory.Options();      
  
            opt.inPreferredConfig = Bitmap.Config.RGB_565;       
  
            opt.inPurgeable = true;      
  
            opt.inInputShareable = true;      
  
            //获取资源图片      
  
            InputStream is = context.getResources().openRawResource(resId);      
  
                return BitmapFactory.decodeStream(is,null,opt);      
  
        } 

如果你的控件大小小于原始图片大小,那么就需要对图片进行压缩处理,来减少内存使用。

现在知道了原图片的尺寸,根据实际情况决定你要加载它缩小多少倍后的图片。例如你用一个128x96的ImageView显示一张1024x768的原图,根本没有必要把原图读加载到内存。
加载一张缩小后的图片到内存,只需要把BitmapFactory.Options对象的inSampleSize设为true,
然后给inSampleSize设一个值就行了(可以理解inSampleSize为n,图片就缩小到1/n大小)。

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,    
        int reqWidth, int reqHeight) {    
    
    // First decode with inJustDecodeBounds=true to check dimensions    
    final BitmapFactory.Options options = new BitmapFactory.Options();    
    options.inJustDecodeBounds = true;    
    BitmapFactory.decodeResource(res, resId, options);    
    
    // Calculate inSampleSize    
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);    
    
    // Decode bitmap with inSampleSize set    
    options.inJustDecodeBounds = false;    
    return BitmapFactory.decodeResource(res, resId, options);    
}    
public static int calculateInSampleSize(    
            BitmapFactory.Options options, int reqWidth, int reqHeight) {    
    // Raw height and width of image    
    final int height = options.outHeight;    
    final int width = options.outWidth;    
    int inSampleSize = 1;    
    
    if (height > reqHeight || width > reqWidth) {    
        if (width > height) {    
            inSampleSize = Math.round((float)height / (float)reqHeight);    
        } else {    
            inSampleSize = Math.round((float)width / (float)reqWidth);    
        }    
    }    
    return inSampleSize;    
}  

方法三:使用内存缓存

对于缓存,没有大小或者规则适用于所有应用,它依赖于你分析自己应用的内存使用确定自己的方案。
缓存太小可能只会增加额外的内存使用,缓存太大可能会导致内存溢出或者应用其它模块可使用内存太小

    private LruCache<String, Bitmap> mMemoryCache;  
      
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        ...  
        // Get max available VM memory, exceeding this amount will throw an  
        // OutOfMemory exception. Stored in kilobytes as LruCache takes an  
        // int in its constructor.  
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);  
      
        // Use 1/8th of the available memory for this memory cache.  
        final int cacheSize = maxMemory / 8;  
      
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {  
            @Override  
            protected int sizeOf(String key, Bitmap bitmap) {  
                // The cache size will be measured in kilobytes rather than  
                // number of items.  
                return bitmap.getByteCount() / 1024;  
            }  
        };  
        ...  
    }  
      
    public void addBitmapToMemoryCache(String key, Bitmap bitmap) {  
        if (getBitmapFromMemCache(key) == null) {  
            mMemoryCache.put(key, bitmap);  
        }  
    }  
      
    public Bitmap getBitmapFromMemCache(String key) {  
        return mMemoryCache.get(key);  
    }  

加载压缩后的图片到ImageView显示

    public void loadBitmap(int resId, ImageView imageView) {  
        final String imageKey = String.valueOf(resId);  
      
        final Bitmap bitmap = getBitmapFromMemCache(imageKey);  
        if (bitmap != null) {  
            mImageView.setImageBitmap(bitmap);  
        } else {  
            mImageView.setImageResource(R.drawable.image_placeholder);  
            BitmapWorkerTask task = new BitmapWorkerTask(mImageView);  
            task.execute(resId);  
        }  
    }  

BitmapWorkerTask加载图片后,也要把图片缓存到内存中:

    class BitmapWorkerTask extends AsyncTask {    
        ...    
        // Decode image in background.    
        @Override    
        protected Bitmap doInBackground(Integer... params) {    
            final Bitmap bitmap = decodeSampledBitmapFromResource(    
                    getResources(), params[0], 100, 100));    
            addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);    
            return bitmap;    
        }    
        ...    
    }    

方法四:使用磁盘缓存

你的应用也有可能被其他任务打断,如电话呼入,应用在后台有可能会被结束,这样缓存的数据也会丢失。
当用户回到应用时,所有的图片还需要重新获取一遍。
磁盘缓存可应用到这种场景中,它可以减少你获取图片的次数,当然,从磁盘获取图片比从内存中获取要慢的多,所以它需要在非UI线程中完成。
示例代码中是磁盘缓存的一个实现,在Android4.0源码中(libcore/luni/src/main/java/libcore/io/DiskLruCache.java),
有更加强大和推荐的一个实现,它的向后兼容使在已发布过的库中很方便使用它

    private DiskLruCache mDiskCache;  
    private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB  
    private static final String DISK_CACHE_SUBDIR = "thumbnails";  
      
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        ...  
        // Initialize memory cache  
        ...  
        File cacheDir = getCacheDir(this, DISK_CACHE_SUBDIR);  
        mDiskCache = DiskLruCache.openCache(this, cacheDir, DISK_CACHE_SIZE);  
        ...  
    }  
      
    class BitmapWorkerTask extends AsyncTask {  
        ...  
        // Decode image in background.  
        @Override  
        protected Bitmap doInBackground(Integer... params) {  
            final String imageKey = String.valueOf(params[0]);  
      
            // Check disk cache in background thread  
            Bitmap bitmap = getBitmapFromDiskCache(imageKey);  
      
            if (bitmap == null) { // Not found in disk cache  
                // Process as normal  
                final Bitmap bitmap = decodeSampledBitmapFromResource(  
                        getResources(), params[0], 100, 100));  
            }  
      
            // Add final bitmap to caches  
            addBitmapToCache(String.valueOf(imageKey, bitmap);  
      
            return bitmap;  
        }  
        ...  
    }  
      
    public void addBitmapToCache(String key, Bitmap bitmap) {  
        // Add to memory cache as before  
        if (getBitmapFromMemCache(key) == null) {  
            mMemoryCache.put(key, bitmap);  
        }  
      
        // Also add to disk cache  
        if (!mDiskCache.containsKey(key)) {  
            mDiskCache.put(key, bitmap);  
        }  
    }  
      
    public Bitmap getBitmapFromDiskCache(String key) {  
        return mDiskCache.get(key);  
    }  
      
    // Creates a unique subdirectory of the designated app cache directory. Tries to use external  
    // but if not mounted, falls back on internal storage.  
    public static File getCacheDir(Context context, String uniqueName) {  
        // Check if media is mounted or storage is built-in, if so, try and use external cache dir  
        // otherwise use internal cache dir  
        final String cachePath = Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED  
                || !Environment.isExternalStorageRemovable() ?  
                        context.getExternalCacheDir().getPath() : context.getCacheDir().getPath();  
      
        return new File(cachePath + File.separator + uniqueName);  
    }  

运行时的配置改变,例如屏幕横竖屏切换了有好的用户体验,你可能不想在这种情况下,重新获取一遍图片。
幸好你可以使用上面讲的内存缓存。缓存可以通过使用一个Fragment(调用setRetainInstance(true)被传到新的Activity,
当新的Activity被创建后,只需要重新附加Fragment,你就可以得到这个Fragment并访问到存在的缓存,把里面的图片快速的显示出来

    private LruCache mMemoryCache;  
      
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        ...  
        RetainFragment mRetainFragment =  
                RetainFragment.findOrCreateRetainFragment(getFragmentManager());  
        mMemoryCache = RetainFragment.mRetainedCache;  
        if (mMemoryCache == null) {  
            mMemoryCache = new LruCache(cacheSize) {  
                ... // Initialize cache here as usual  
            }  
            mRetainFragment.mRetainedCache = mMemoryCache;  
        }  
        ...  
    }  
      
    class RetainFragment extends Fragment {  
        private static final String TAG = "RetainFragment";  
        public LruCache mRetainedCache;  
      
        public RetainFragment() {}  
      
        public static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {  
            RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG);  
            if (fragment == null) {  
                fragment = new RetainFragment();  
            }  
            return fragment;  
        }  
      
        @Override  
        public void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
            setRetainInstance(true);  
        }  
    }  





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值