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

方法一:

  1. public Bitmap decodeFile(String filePath) {
  2. Bitmap bitmap = null;
  3. BitmapFactory.Options options = new BitmapFactory.Options();
  4. options.inPurgeable = true;
  5. try {
  6. BitmapFactory.Options.class.getField("inNativeAlloc").setBoolean(
  7. options, true);
  8. } catch (IllegalArgumentException e) {
  9. e.printStackTrace();
  10. } catch (SecurityException e) {
  11. e.printStackTrace();
  12. } catch (IllegalAccessException e) {
  13. e.printStackTrace();
  14. } catch (NoSuchFieldException e) {
  15. e.printStackTrace();
  16. }
  17. if (mFilePath != null) {
  18. bitmap = BitmapFactory.decodeFile(mFilePath, options);
  19. }
  20. return bitmap;
  21. }

方法二:

  1. public Bitmap ReadBitMap(Context context, int resId){
  2.  
  3. BitmapFactory.Options opt = new BitmapFactory.Options();
  4.  
  5. opt.inPreferredConfig = Bitmap.Config.RGB_565;
  6.  
  7. opt.inPurgeable = true;
  8.  
  9. opt.inInputShareable = true;
  10.  
  11. //获取资源图片
  12.  
  13. InputStream is = context.getResources().openRawResource(resId);
  14.  
  15. return BitmapFactory.decodeStream(is,null,opt);
  16.  
  17. }

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

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

  1. public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
  2. int reqWidth, int reqHeight) {
  3.  
  4. // First decode with inJustDecodeBounds=true to check dimensions
  5. final BitmapFactory.Options options = new BitmapFactory.Options();
  6. options.inJustDecodeBounds = true;
  7. BitmapFactory.decodeResource(res, resId, options);
  8.  
  9. // Calculate inSampleSize
  10. options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
  11.  
  12. // Decode bitmap with inSampleSize set
  13. options.inJustDecodeBounds = false;
  14. return BitmapFactory.decodeResource(res, resId, options);
  15. }
  16. public static int calculateInSampleSize(
  17. BitmapFactory.Options options, int reqWidth, int reqHeight) {
  18. // Raw height and width of image
  19. final int height = options.outHeight;
  20. final int width = options.outWidth;
  21. int inSampleSize = 1;
  22.  
  23. if (height > reqHeight || width > reqWidth) {
  24. if (width > height) {
  25. inSampleSize = Math.round((float)height / (float)reqHeight);
  26. } else {
  27. inSampleSize = Math.round((float)width / (float)reqWidth);
  28. }
  29. }
  30. return inSampleSize;
  31. }

方法三:使用内存缓存

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

  1. private LruCache<String, Bitmap> mMemoryCache;
  2.  
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. ...
  6. // Get max available VM memory, exceeding this amount will throw an
  7. // OutOfMemory exception. Stored in kilobytes as LruCache takes an
  8. // int in its constructor.
  9. final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
  10.  
  11. // Use 1/8th of the available memory for this memory cache.
  12. final int cacheSize = maxMemory / 8;
  13.  
  14. mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
  15. @Override
  16. protected int sizeOf(String key, Bitmap bitmap) {
  17. // The cache size will be measured in kilobytes rather than
  18. // number of items.
  19. return bitmap.getByteCount() / 1024;
  20. }
  21. };
  22. ...
  23. }
  24.  
  25. public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
  26. if (getBitmapFromMemCache(key) == null) {
  27. mMemoryCache.put(key, bitmap);
  28. }
  29. }
  30.  
  31. public Bitmap getBitmapFromMemCache(String key) {
  32. return mMemoryCache.get(key);
  33. }

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

  1. public void loadBitmap(int resId, ImageView imageView) {
  2. final String imageKey = String.valueOf(resId);
  3.  
  4. final Bitmap bitmap = getBitmapFromMemCache(imageKey);
  5. if (bitmap != null) {
  6. mImageView.setImageBitmap(bitmap);
  7. } else {
  8. mImageView.setImageResource(R.drawable.image_placeholder);
  9. BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
  10. task.execute(resId);
  11. }
  12. }

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

  1. class BitmapWorkerTask extends AsyncTask {
  2. ...
  3. // Decode image in background.
  4. @Override
  5. protected Bitmap doInBackground(Integer... params) {
  6. final Bitmap bitmap = decodeSampledBitmapFromResource(
  7. getResources(), params[0], 100, 100));
  8. addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
  9. return bitmap;
  10. }
  11. ...
  12. }

方法四:使用磁盘缓存

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

  1. private DiskLruCache mDiskCache;
  2. private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB
  3. private static final String DISK_CACHE_SUBDIR = "thumbnails";
  4.  
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState) {
  7. ...
  8. // Initialize memory cache
  9. ...
  10. File cacheDir = getCacheDir(this, DISK_CACHE_SUBDIR);
  11. mDiskCache = DiskLruCache.openCache(this, cacheDir, DISK_CACHE_SIZE);
  12. ...
  13. }
  14.  
  15. class BitmapWorkerTask extends AsyncTask {
  16. ...
  17. // Decode image in background.
  18. @Override
  19. protected Bitmap doInBackground(Integer... params) {
  20. final String imageKey = String.valueOf(params[0]);
  21.  
  22. // Check disk cache in background thread
  23. Bitmap bitmap = getBitmapFromDiskCache(imageKey);
  24.  
  25. if (bitmap == null) { // Not found in disk cache
  26. // Process as normal
  27. final Bitmap bitmap = decodeSampledBitmapFromResource(
  28. getResources(), params[0], 100, 100));
  29. }
  30.  
  31. // Add final bitmap to caches
  32. addBitmapToCache(String.valueOf(imageKey, bitmap);
  33.  
  34. return bitmap;
  35. }
  36. ...
  37. }
  38.  
  39. public void addBitmapToCache(String key, Bitmap bitmap) {
  40. // Add to memory cache as before
  41. if (getBitmapFromMemCache(key) == null) {
  42. mMemoryCache.put(key, bitmap);
  43. }
  44.  
  45. // Also add to disk cache
  46. if (!mDiskCache.containsKey(key)) {
  47. mDiskCache.put(key, bitmap);
  48. }
  49. }
  50.  
  51. public Bitmap getBitmapFromDiskCache(String key) {
  52. return mDiskCache.get(key);
  53. }
  54.  
  55. // Creates a unique subdirectory of the designated app cache directory. Tries to use external
  56. // but if not mounted, falls back on internal storage.
  57. public static File getCacheDir(Context context, String uniqueName) {
  58. // Check if media is mounted or storage is built-in, if so, try and use external cache dir
  59. // otherwise use internal cache dir
  60. final String cachePath = Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
  61. || !Environment.isExternalStorageRemovable() ?
  62. context.getExternalCacheDir().getPath() : context.getCacheDir().getPath();
  63.  
  64. return new File(cachePath + File.separator + uniqueName);
  65. }

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

  1. private LruCache mMemoryCache;
  2.  
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. ...
  6. RetainFragment mRetainFragment =
  7. RetainFragment.findOrCreateRetainFragment(getFragmentManager());
  8. mMemoryCache = RetainFragment.mRetainedCache;
  9. if (mMemoryCache == null) {
  10. mMemoryCache = new LruCache(cacheSize) {
  11. ... // Initialize cache here as usual
  12. }
  13. mRetainFragment.mRetainedCache = mMemoryCache;
  14. }
  15. ...
  16. }
  17.  
  18. class RetainFragment extends Fragment {
  19. private static final String TAG = "RetainFragment";
  20. public LruCache mRetainedCache;
  21.  
  22. public RetainFragment() {}
  23.  
  24. public static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {
  25. RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG);
  26. if (fragment == null) {
  27. fragment = new RetainFragment();
  28. }
  29. return fragment;
  30. }
  31.  
  32. @Override
  33. public void onCreate(Bundle savedInstanceState) {
  34. super.onCreate(savedInstanceState);
  35. setRetainInstance(true);
  36. }
  37. }

关于android 图片加载优化的更多相关文章

  1. android 图片加载优化,避免oom问题产生

    1,及时回收bitmap,在activity的onstop()和onDestory()里面调用如下代码进行bitmap的回收: // 先判断是否已经回收 if(bitmap != null & ...

  2. Android图片加载库的理解

    前言     这是“基础自测”系列的第三篇文章,以Android开发需要熟悉的20个技术点为切入点,本篇重点讲讲Android中的ImageLoader这个库的一些理解,在Android上最让人头疼是 ...

  3. 一起写一个Android图片加载框架

    本文会从内部原理到具体实现来详细介绍如何开发一个简洁而实用的Android图片加载缓存框架,并在内存占用与加载图片所需时间这两个方面与主流图片加载框架之一Universal Image Loader做 ...

  4. fackbook的Fresco (FaceBook推出的Android图片加载库-Fresco)

    [Android开发经验]FaceBook推出的Android图片加载库-Fresco   欢迎关注ndroid-tech-frontier开源项目,定期翻译国外Android优质的技术.开源库.软件 ...

  5. Android 图片加载库Glide 实战(二),占位符,缓存,转换自签名高级实战

    http://blog.csdn.net/sk719887916/article/details/40073747 请尊重原创 : skay <Android 图片加载库Glide 实战(一), ...

  6. Android图片加载框架最全解析(八),带你全面了解Glide 4的用法

    本篇将是我们这个Glide系列的最后一篇文章. 其实在写这个系列第一篇文章的时候,Glide就推出4.0.0的RC版了.那个时候因为我一直研究的都是Glide 3.7.0版本,再加上RC版本还不太稳定 ...

  7. Android图片加载框架最全解析(七),实现带进度的Glide图片加载功能

    我们的Glide系列文章终于要进入收尾篇了.从我开始写这个系列的第一篇文章时,我就知道这会是一个很长的系列,只是没有想到竟然会写这么久. 在前面的六篇文章中,我们对Glide的方方面面都进行了学习,包 ...

  8. Android图片加载框架最全解析(一),Glide的基本用法

    现在Android上的图片加载框架非常成熟,从最早的老牌图片加载框架UniversalImageLoader,到后来Google推出的Volley,再到后来的新兴军Glide和Picasso,当然还有 ...

  9. FaceBook推出的Android图片加载库-Fresco

    FaceBook推出的Android图片加载库-Fresco 原文链接:Introducing Fresco: A new image library for Android 译者 : ZhaoKai ...

随机推荐

  1. MyEclipse中Save could not be completed

    在MyEclipse下编程时,保存的时候,假设出现例如以下图所看到的错误: - 刘立 - 707903908的博客" src="http://img0.ph.126.net/9y4 ...

  2. 谷歌高管无意中透露Google Glass未获得成功的原因

    Google X高管Astro Teller在接受媒体采访时无意中透露了这款设备没有取得预期成绩的原因 最终我们发现,在他们生活的世界里,数字生活和即时物理生活根本无法融为一体. Teller提出的这 ...

  3. PHP-Manual的学习----【语言参考】----【类型】-----【Boolean类型】

    2017年7月20日15:41:26Boolean 布尔类型 1.这是最简单的类型.boolean 表达了真值,可以为 TRUE 或 FALSE. 其实就是真假的问题.2.语法 要指定一个布尔值,使用 ...

  4. commons-dbutils:1.6 ——java.sql.SQLException: 不支持的特性

    描述:使用jdbc创建连接后,使用commons-dbutils-1.6 数据库工具类,查询报错如下:java.sql.SQLException: 不支持的特性 Query: 经过测试跟踪在commo ...

  5. jquery获取页面iframe内容

    //取得整个HTML格式 var f = $(window.frames["ReportIFrame"].document).contents().html(); 或者 $(&qu ...

  6. Gaby Ivanushka(快排)

    Gaby Ivanushka Once upon a time there lived a tsar that has a daughter — Beautiful Vasilisa. There w ...

  7. maven3 org.codehaus.plexus.classworlds.launcher.launcher 找不到或无法加载主类

    maven3 org.codehaus.plexus.classworlds.launcher.launcher 找不到或无法加载主类 嗯,网上很多资料说是路径的问题,确实是有可能是路径的问题,而且还 ...

  8. Lattice绘图

    lattice包提供了用于可视化单变量和多变量数据的一整套图形系统.许多用户转向使用lattice包是因为它能很容易地生成网格图形.网格图形能够展示变量的分布或变量之间的关系. 例:data(mtca ...

  9. Android:日常学习笔记(10)———使用LitePal操作数据库

    Android:日常学习笔记(10)———使用LitePal操作数据库 引入LitePal 什么是LitePal LitePal是一款开源的Android数据库框架,采用了对象关系映射(ORM)的模式 ...

  10. Thread.currentThread().getContextClassLoader() and Class.getClassLoader()

    Thread.currentThread().getContextClassLoader() and Class.getClassLoader()   一.同一工程中: String path = T ...