Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 504|回复: 0

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

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-7-3 11:01:03 | 显示全部楼层 |阅读模式

      低内存的手机如果直接加载大图片,往往会出现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;
        }
    View Code

      关键代码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
    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-1-11 13:02 , Processed in 0.064476 second(s), 30 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表