关于android 图片加载优化

来源:互联网 发布:怎么设置域名跳转 编辑:程序博客网 时间:2024/06/06 15:44


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;   //小于ARGB_8888  样式大小            opt.inPurgeable = true;                opt.inInputShareable = true;                //获取资源图片                InputStream is = context.getResources().openRawResource(resId);                    return BitmapFactory.decodeStream(is,null,opt);            }    


how to calucate bitmap size

int calculateBitmapSize(Bitmap candidate,Resources res, int resId){BitmapFactory.Options targetOptions=new BitmapFactory.Options();targetOptions.inJustDecodeBounds = true;BitmapFactory.decodeResource(res, resId, targetOptions);int width = targetOptions.outWidth / targetOptions.inSampleSize;        int height = targetOptions.outHeight / targetOptions.inSampleSize;        int byteCount = width * height * getBytesPerPixel(candidate.getConfig());        return byteCount;}/** * A helper function to return the byte usage per pixel of a bitmap based on its configuration. */static int getBytesPerPixel(Config config) {    if (config == Config.ARGB_8888) {        return 4;    } else if (config == Config.RGB_565) {        return 2;    } else if (config == Config.ARGB_4444) {        return 2;    } else if (config == Config.ALPHA_8) {        return 1;    }    return 1;}


指定尺寸压缩:

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) {        final int halfHeight = height / 2;        final int halfWidth = width / 2;        // Calculate the largest inSampleSize value that is a power of 2 and keeps both        // height and width larger than the requested height and width.        while ((halfHeight / inSampleSize) > reqHeight                && (halfWidth / inSampleSize) > reqWidth) {            inSampleSize *= 2;        }    }    return inSampleSize;}

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);}


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

现在知道了原图片的尺寸,根据实际情况决定你要加载它缩小多少倍后的图片。例如你用一个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;  }  


官方压缩计算:更新2016-07-16

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) {        final int halfHeight = height / 2;        final int halfWidth = width / 2;        // Calculate the largest inSampleSize value that is a power of 2 and keeps both        // height and width larger than the requested height and width.        while ((halfHeight / inSampleSize) >= reqHeight                && (halfWidth / inSampleSize) >= reqWidth) {            inSampleSize *= 2;        }    }    return inSampleSize;}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);}


方法三:使用内存缓存

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

private LruCache<String, Bitmap> mMemoryCache;@Overrideprotected 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; // 10MBprivate static final String DISK_CACHE_SUBDIR = "thumbnails";@Overrideprotected 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;@Overrideprotected 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);    }}






原创粉丝点击