android图片加载内存优化方法,有效解决大图片内存溢出(oom)

  低内存的手机如果直接加载大图片,往往会出现OOM的情况.即便是主流手机,也不能无限制的加载大图片.所以在显示图片之前,需要对图片处理,把图片缩放为最合适的尺寸再显示.

  网上很大方法都是不管三七二十一,直接压缩图片.这样可能会导致图片失真,显示模糊.我采用的方式是,显示尺寸有多大,就等比例压缩成多大尺寸的图片,关键关于在于如何寻找最合适的尺寸,下面分享两个关键方法,提取至google开源框架volley

    private static int getResizedDimension(int maxPrimary, int maxSecondary,
            int actualPrimary, int actualSecondary) {
        // If no dominant value at all, just return the actual.
        if (maxPrimary == 0 && maxSecondary == 0) {
            return actualPrimary;
        }

        // If primary is unspecified, scale primary to match secondary‘s scaling
        // ratio.
        if (maxPrimary == 0) {
            double ratio = (double) maxSecondary / (double) actualSecondary;
            return (int) (actualPrimary * ratio);
        }

        if (maxSecondary == 0) {
            return maxPrimary;
        }

        double ratio = (double) actualSecondary / (double) actualPrimary;
        int resized = maxPrimary;
        if (resized * ratio > maxSecondary) {
            resized = (int) (maxSecondary / ratio);
        }
        return resized;
    }

    private static int findBestSampleSize(int actualWidth, int actualHeight,
            int desiredWidth, int desiredHeight) {
        double wr = (double) actualWidth / desiredWidth;
        double hr = (double) actualHeight / desiredHeight;
        double ratio = Math.min(wr, hr);
        float n = 1.0f;
        while ((n * 2) <= ratio) {
            n *= 2;
        }

        return (int) n;
    }

  图片源可能来至不同的地方,如Assets,byte[],Bitmap,Uri,Resource,File.针对不同的源采用不同的方法,但是大同小异

public static Bitmap getImageFromAssetsFile(String fileName, int mMaxWidth,
            int mMaxHeight) {
        BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
        Bitmap bitmap = null;
        Config preferredConfig = Config.RGB_565;
        AssetManager am = Global.mAppCxt.getResources().getAssets();
        try {
            if (mMaxWidth == 0 && mMaxHeight == 0) {
                decodeOptions.inPreferredConfig = preferredConfig;
                InputStream is = am.open(fileName);
                bitmap = BitmapFactory.decodeStream(is, null, decodeOptions);
                is.close();
            } else {
                // If we have to resize this image, first get the natural
                // bounds.
                decodeOptions.inJustDecodeBounds = true;
                decodeOptions.inPreferredConfig = preferredConfig;
                InputStream is = am.open(fileName);
                BitmapFactory.decodeStream(is, null, decodeOptions);
                is.close();
                int actualWidth = decodeOptions.outWidth;
                int actualHeight = decodeOptions.outHeight;

                // Then compute the dimensions we would ideally like to decode
                // to.
                int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight,
                        actualWidth, actualHeight);
                int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth,
                        actualHeight, actualWidth);

                // Decode to the nearest power of two scaling factor.
                decodeOptions.inJustDecodeBounds = false;
                // TODO(ficus): Do we need this or is it okay since API 8
                // doesn‘t
                // support it?
                // decodeOptions.inPreferQualityOverSpeed =
                // PREFER_QUALITY_OVER_SPEED;
                decodeOptions.inSampleSize = findBestSampleSize(actualWidth,
                        actualHeight, desiredWidth, desiredHeight);
                decodeOptions.inPreferredConfig = preferredConfig;
                InputStream is2 = am.open(fileName);
                Bitmap tempBitmap = BitmapFactory.decodeStream(is2, null,
                        decodeOptions);
                is2.close();
                // If necessary, scale down to the maximal acceptable size.
                if (tempBitmap != null
                        && (tempBitmap.getWidth() > desiredWidth || tempBitmap
                                .getHeight() > desiredHeight)) {
                    bitmap = Bitmap.createScaledBitmap(tempBitmap,
                            desiredWidth, desiredHeight, true);
                    tempBitmap.recycle();
                } else {
                    bitmap = tempBitmap;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bitmap;
    }

    public static Bitmap getImageFromData(byte[] data, int mMaxWidth,
            int mMaxHeight) {
        BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
        Bitmap bitmap = null;
        if (mMaxWidth == 0 && mMaxHeight == 0) {
            decodeOptions.inPreferredConfig = Config.RGB_565;
            bitmap = BitmapFactory.decodeByteArray(data, 0, data.length,
                    decodeOptions);
        } else {
            // If we have to resize this image, first get the natural bounds.
            decodeOptions.inJustDecodeBounds = true;
            BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
            int actualWidth = decodeOptions.outWidth;
            int actualHeight = decodeOptions.outHeight;

            // Then compute the dimensions we would ideally like to decode to.
            int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight,
                    actualWidth, actualHeight);
            int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth,
                    actualHeight, actualWidth);

            // Decode to the nearest power of two scaling factor.
            decodeOptions.inJustDecodeBounds = false;
            // TODO(ficus): Do we need this or is it okay since API 8 doesn‘t
            // support it?
            // decodeOptions.inPreferQualityOverSpeed =
            // PREFER_QUALITY_OVER_SPEED;
            decodeOptions.inSampleSize = findBestSampleSize(actualWidth,
                    actualHeight, desiredWidth, desiredHeight);
            Bitmap tempBitmap = BitmapFactory.decodeByteArray(data, 0,
                    data.length, decodeOptions);

            // If necessary, scale down to the maximal acceptable size.
            if (tempBitmap != null
                    && (tempBitmap.getWidth() > desiredWidth || tempBitmap
                            .getHeight() > desiredHeight)) {
                bitmap = Bitmap.createScaledBitmap(tempBitmap, desiredWidth,
                        desiredHeight, true);
                tempBitmap.recycle();
            } else {
                bitmap = tempBitmap;
            }
        }
        return bitmap;
    }

    public static Bitmap getImageFromBitmap(Bitmap srcBitmap, int mMaxWidth,
            int mMaxHeight) {
        Bitmap bitmap = null;
        if (mMaxWidth == 0 && mMaxHeight == 0) {
            bitmap = srcBitmap;
        } else {
            int actualWidth = srcBitmap.getWidth();
            int actualHeight = srcBitmap.getHeight();

            // Then compute the dimensions we would ideally like to decode to.
            int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight,
                    actualWidth, actualHeight);
            int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth,
                    actualHeight, actualWidth);
            bitmap = Bitmap.createScaledBitmap(srcBitmap, desiredWidth,
                    desiredHeight, true);
        }
        return bitmap;
    }

    public static Bitmap getImageFromUri(Uri uri, int mMaxWidth,
            int mMaxHeight) {
        BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
        Bitmap bitmap = null;
        Config preferredConfig = Config.RGB_565;
        try {
            if (mMaxWidth == 0 && mMaxHeight == 0) {
                decodeOptions.inPreferredConfig = preferredConfig;
                InputStream is = Global.mAppCxt.getContentResolver()
                        .openInputStream(uri);
                bitmap = BitmapFactory.decodeStream(is, null, decodeOptions);
                is.close();
            } else {
                // If we have to resize this image, first get the natural
                // bounds.
                decodeOptions.inJustDecodeBounds = true;
                decodeOptions.inPreferredConfig = preferredConfig;
                InputStream is = Global.mAppCxt.getContentResolver()
                        .openInputStream(uri);
                BitmapFactory.decodeStream(is, null, decodeOptions);
                is.close();
                int actualWidth = decodeOptions.outWidth;
                int actualHeight = decodeOptions.outHeight;

                // Then compute the dimensions we would ideally like to decode
                // to.
                int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight,
                        actualWidth, actualHeight);
                int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth,
                        actualHeight, actualWidth);

                // Decode to the nearest power of two scaling factor.
                decodeOptions.inJustDecodeBounds = false;
                // TODO(ficus): Do we need this or is it okay since API 8
                // doesn‘t
                // support it?
                // decodeOptions.inPreferQualityOverSpeed =
                // PREFER_QUALITY_OVER_SPEED;
                decodeOptions.inSampleSize = findBestSampleSize(actualWidth,
                        actualHeight, desiredWidth, desiredHeight);
                decodeOptions.inPreferredConfig = preferredConfig;
                InputStream is2 = Global.mAppCxt.getContentResolver()
                        .openInputStream(uri);
                Bitmap tempBitmap = BitmapFactory.decodeStream(is2, null,
                        decodeOptions);
                is2.close();
                // If necessary, scale down to the maximal acceptable size.
                if (tempBitmap != null
                        && (tempBitmap.getWidth() > desiredWidth || tempBitmap
                                .getHeight() > desiredHeight)) {
                    bitmap = Bitmap.createScaledBitmap(tempBitmap,
                            desiredWidth, desiredHeight, true);
                    tempBitmap.recycle();
                } else {
                    bitmap = tempBitmap;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bitmap;
    }

    public static Bitmap getImageFromResource(int resId, int mMaxWidth,
            int mMaxHeight) {
        BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
        Bitmap bitmap = null;
        Config preferredConfig = Config.RGB_565;
        try {
            if (mMaxWidth == 0 && mMaxHeight == 0) {
                decodeOptions.inPreferredConfig = preferredConfig;
                InputStream is = Global.mAppCxt.getResources().openRawResource(
                        resId);
                bitmap = BitmapFactory.decodeStream(is, null, decodeOptions);
                is.close();
            } else {
                // If we have to resize this image, first get the natural
                // bounds.
                decodeOptions.inJustDecodeBounds = true;
                decodeOptions.inPreferredConfig = preferredConfig;
                InputStream is = Global.mAppCxt.getResources().openRawResource(
                        resId);
                BitmapFactory.decodeStream(is, null, decodeOptions);
                is.close();
                int actualWidth = decodeOptions.outWidth;
                int actualHeight = decodeOptions.outHeight;

                // Then compute the dimensions we would ideally like to decode
                // to.
                int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight,
                        actualWidth, actualHeight);
                int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth,
                        actualHeight, actualWidth);

                // Decode to the nearest power of two scaling factor.
                decodeOptions.inJustDecodeBounds = false;
                // TODO(ficus): Do we need this or is it okay since API 8
                // doesn‘t
                // support it?
                // decodeOptions.inPreferQualityOverSpeed =
                // PREFER_QUALITY_OVER_SPEED;
                decodeOptions.inSampleSize = findBestSampleSize(actualWidth,
                        actualHeight, desiredWidth, desiredHeight);
                decodeOptions.inPreferredConfig = preferredConfig;
                InputStream is2 = Global.mAppCxt.getResources()
                        .openRawResource(resId);
                Bitmap tempBitmap = BitmapFactory.decodeStream(is2, null,
                        decodeOptions);
                is2.close();
                // If necessary, scale down to the maximal acceptable size.
                if (tempBitmap != null
                        && (tempBitmap.getWidth() > desiredWidth || tempBitmap
                                .getHeight() > desiredHeight)) {
                    bitmap = Bitmap.createScaledBitmap(tempBitmap,
                            desiredWidth, desiredHeight, true);
                    tempBitmap.recycle();
                } else {
                    bitmap = tempBitmap;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bitmap;
    }

    public static Bitmap getImageFromFile(File file, int mMaxWidth,
            int mMaxHeight) {
        BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
        Bitmap bitmap = null;
        Config preferredConfig = Config.RGB_565;
        try {
            if (mMaxWidth == 0 && mMaxHeight == 0) {
                bitmap = BitmapFactory.decodeFile(file.getPath());
            } else {
                // If we have to resize this image, first get the natural
                // bounds.
                decodeOptions.inJustDecodeBounds = true;
                decodeOptions.inPreferredConfig = preferredConfig;
                bitmap = BitmapFactory
                        .decodeFile(file.getPath(), decodeOptions);
                int actualWidth = decodeOptions.outWidth;
                int actualHeight = decodeOptions.outHeight;

                // Then compute the dimensions we would ideally like to decode
                // to.
                int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight,
                        actualWidth, actualHeight);
                int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth,
                        actualHeight, actualWidth);

                // Decode to the nearest power of two scaling factor.
                decodeOptions.inJustDecodeBounds = false;
                // TODO(ficus): Do we need this or is it okay since API 8
                // doesn‘t
                // support it?
                // decodeOptions.inPreferQualityOverSpeed =
                // PREFER_QUALITY_OVER_SPEED;
                decodeOptions.inSampleSize = findBestSampleSize(actualWidth,
                        actualHeight, desiredWidth, desiredHeight);
                decodeOptions.inPreferredConfig = preferredConfig;
                Bitmap tempBitmap = BitmapFactory.decodeFile(file.getPath(),
                        decodeOptions);
                // If necessary, scale down to the maximal acceptable size.
                if (tempBitmap != null
                        && (tempBitmap.getWidth() > desiredWidth || tempBitmap
                                .getHeight() > desiredHeight)) {
                    bitmap = Bitmap.createScaledBitmap(tempBitmap,
                            desiredWidth, desiredHeight, true);
                    tempBitmap.recycle();
                } else {
                    bitmap = tempBitmap;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bitmap;
    }

  关键代码decodeOptions.inJustDecodeBounds = true;指得是只绘制边界,不直接加载图片,通过边界尺寸再结合之前的两个方法计算最合适的尺寸后才加载图片

  Config preferredConfig = Config.RGB_565;不支持透明度,如果需要透明度,就替换成ARGB_8888,但是内存占用会增加一倍.具体可以参考源码的解释

  RGB_565,Each pixel is stored on 2 bytes

  ARGB_8888,Each pixel is stored on 4 bytes

时间: 2024-10-10 22:02:39

android图片加载内存优化方法,有效解决大图片内存溢出(oom)的相关文章

Android中加载位图的方法

Android中加载位图的关键的代码: AssetManager assets =context.getAssets(); //用一个AssetManager 对象来从应用程序包的已编译资源中为工程加载资产 InputStream istream=assets.open("/*位图的名字*/"); BitmapFactory.Options options=new BitmapFactory.Options(); options.inPreferredConfig =Bitmap.Co

Android 高效加载大图,多图解决方案,有效避免程序OOM

高效加载大图片 我们在编写Android程序的时候经常要用到许多图片,不同图片总是会有不同的形状.不同的大小,但在大多数情况下,这些图片都会大于我们程序所需要的大小.比如说系统图片库里展示的图片大都是用手机摄像头拍出来的,这些图片的分辨率会比我们手机屏幕的分辨率高得多.大家应该知道,我们编写的应用程序都是有一定内存限制的,程序占用了过高的内存就容易出现OOM(OutOfMemory)异常.我们可以通过下面的代码看出每个应用程序最高可用内存是多少. [java] view plaincopy in

Android 异步加载图像优化,如:引入线程池、引入缓存

关于Android 从网络上异步加载图像: 个人总结,重在分享! 异步加载图像,由于Adnroid Ui 更新支持单一线程原则,所以从网络上取数据并更新到界面上,为了不阻塞主线程 首先要想到以下方法. 1.在主线程中 new 一个Handler对象,加载图像(优化) 示1:private void  loadImage(final String url, final int id){ handler.post(new Runnable(){ public void run(){ Drawable

彻底解决Android因加载多个大图引起的OutOfMemoryError,内存溢出的问题

http://blog.csdn.net/wulianghuan/article/details/11548373?reload 最近因为项目里需求是选择或者拍摄多张照片后,提供滑动预览和上传,很多照片是好几MB一张,因为目前的Android系统对运行的程序都有一定的内存限制,一般是16MB或24MB(视平台而定),不做处理直接加载的话必然会报OOM (Out Of Memmory).网上有很多解决android加载bitmap内存溢出的方法,我总结了一个通用的方法,下面是我从的开发案例抽取出来

js 图片加载失败处理方法

在项目中不可避免会用到图片,尤其是列表,有时候图片会加载失败:这样就会显示一个很难看的坏图片缩略图:下面介绍两种方法,解决这个问题: 1.如果在你的项目中有引入jQuery插件,你可以使用error([[data],fn])这个函数: $("img").error(function(){ //当图片加载失败时,你要进行的操作 //$(this).attr('src','images/no_pic.jpg'); }); 2.如果项目中没有jQuery这样的插件,可以使用HTML的DOM事

Android高效加载大图、多图解决方案,有效避免程序OOM(转)

本篇文章主要内容来自于Android Doc,我翻译之后又做了些加工,英文好的朋友也可以直接去读原文. http://developer.android.com/training/displaying-bitmaps/index.html 高效加载大图片 我们在编写Android程序的时候经常要用到许多图片,不同图片总是会有不同的形状.不同的大小,但在大多数情况下,这些图片都会大于我们程序所需要的大小.比如说系统图片库里展示的图片大都是用手机摄像头拍出来的,这些图片的分辨率会比我们手机屏幕的分辨

【转】解决Android因加载多个大图引起的OutOfMemoryError,内存溢出的问题

本文来自:http://blog.csdn.net/wulianghuan/article/details/11548373,感谢原作者的分享. 目标是读取SD卡中的图片并且展示出来 主要思路是通过一个工具类来压缩来自sd卡中的图片,最后通过缓存机制来避免占用过多内存. Util.java package com.kale.socketactivity; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; i

Android高效加载大图、多图解决方案,有效避免程序OOM

我们可以通过下面的代码看出每个应用程序最高可用内存是多少 int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);   Log.d("TAG", "Max memory is " + maxMemory + "KB"); BitmapFactory这个类提供了多个解析方法(decodeByteArray, decodeFile, decodeResource等)用于创建Bit

img中图片加载不出时默认显示的图片

在js中有onload.onerror两个事件,可在图片中加入,修改对于的src地址, 这样可改善网络差或者图片丢失时的用户体验 如下 <img  src="img/3.jpg"  onerror="this.src='error.jpg'"/>