美文网首页
关于BitmapFactory.decode

关于BitmapFactory.decode

作者: EvilsoulM | 来源:发表于2015-11-04 23:13 被阅读3517次

    最近使用picasso遇到一个问题,picasso load在某些机子上load本地图片load不出来java.io.IOException: Cannot reset picasso官方也没有解决这个问题,只好自己写一个加载本地资源的库,也遇到了不少坑

    Decode内存占用过大

    这个问题还是由于自己对于BitmapFactory decode了解的不够仔细,先贴出原始代码吧

    
        /**
         * 根据imageview宽高 decode 图片
         */
        public static Bitmap decodeStream(InputStream is, ImageView imageView) {
            BitmapFactory.Options opts = new BitmapFactory.Options();
            opts.inJustDecodeBounds = true;
    
            //获取imageview想要显示的宽和高
            ImageSize imageViewSize = getImageViewSize(imageView);
            opts.inSampleSize = ImageUtils.calculateInSampleSize(opts,
                    imageViewSize.getWidth(), imageViewSize.getHeight());
    
            opts.inJustDecodeBounds = false;
    
            return BitmapFactory.decodeStream(is, null, opts);
        }
    

    这段代码貌似乍看没什么问题,考虑到了imageview加载过大图片的问题(ImageUtils.calculateInSampleSize),也用了inJustDecodeBounds属性,在native decode 不生成bitmap,获取bitmap宽和高,根据宽高压缩图片。

    运行的时候发现,内存一个劲的上升,查了半天才发现,少写了一句decode...

    
        /**
         * 根据imageview宽高 decode 图片
         */
        public static Bitmap decodeStream(InputStream is, ImageView imageView) {
            BitmapFactory.Options opts = new BitmapFactory.Options();
            opts.inJustDecodeBounds = true;
            //这行代码,如果没有这行代码,native就不会decode流,其实很简单
            //低下你调用什么decode在inJustDecodeBounds = true;下面你就要写一样的方法先在native decode bitmap获取宽高
            BitmapFactory.decodeStream(is, null, opts);
    
            //获取imageview想要显示的宽和高
            ImageSize imageViewSize = getImageViewSize(imageView);
            opts.inSampleSize = ImageUtils.calculateInSampleSize(opts,
                    imageViewSize.getWidth(), imageViewSize.getHeight());
    
            opts.inJustDecodeBounds = false;
    
            return BitmapFactory.decodeStream(is, null, opts);
        }
    

    Android: SkImageDecoder:: Factory returned null

    解决了上面一个问题后,图片还是load不出来...也没有什么异常或者crash的日志,只看到logcat 里面解析图片的时候会报Android: SkImageDecoder:: Factory returned null 查了下说是 “第一次取图片尺寸的时候is这个InputStream被使用过了,再真正取图片的时候又使用了这个InputStream,此时流的起始位置已经被移动过了,需要调用is.reset()来重置,然后再decodeStream(imgInputStream, null, options)就没问题了。 ”
    于是乎就将代码改为

    
        /**
         * 根据imageview宽高 decode 图片
         */
        public static Bitmap decodeStream(InputStream is, ImageView imageView) {
            BitmapFactory.Options opts = new BitmapFactory.Options();
            opts.inJustDecodeBounds = true;
    
            BitmapFactory.decodeStream(is, null, opts);
            is.reset();//加入reset
           
            ImageSize imageViewSize = getImageViewSize(imageView);
            opts.inSampleSize = ImageUtils.calculateInSampleSize(opts,
                    imageViewSize.getWidth(), imageViewSize.getHeight());
    
            opts.inJustDecodeBounds = false;
    
            return BitmapFactory.decodeStream(is, null, opts);
        }
    

    java.io.IOException

    加入reset之后又遇到IOException 查了些也没具体说为什么就改成下面的方法了,果然好了

     /**
         * 根据imageview宽高 decode 图片
         */
        public static Bitmap decodeStream(InputStream is, ImageView imageView) {
            Bitmap bitmap = null;
            BufferedInputStream buffer = null;
    
            BitmapFactory.Options opts = new BitmapFactory.Options();
            opts.inJustDecodeBounds = true;
    
            try {
                int buffersize = 16 * 1024;
                buffer = new BufferedInputStream(is, buffersize);
                buffer.mark(16 * 1024);
                BitmapFactory.decodeStream(buffer, null, opts);
                buffer.reset();
    
                //获取imageview想要显示的宽和高
                ImageSize imageViewSize = getImageViewSize(imageView);
                opts.inSampleSize = ImageUtils.calculateInSampleSize(opts,
                        imageViewSize.getWidth(), imageViewSize.getHeight());
    
                opts.inJustDecodeBounds = false;
                bitmap = BitmapFactory.decodeStream(buffer, null, opts);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                ImageUtils.closeQuietly(buffer);
                ImageUtils.closeQuietly(is);
            }
            return bitmap;
        }
    

    但是,第二天使用oppo R7的时候还是报哪个错误,load不出来图。逐改成,确实解决了问题,都能顺利的decode图正常显示了。

    /**
         * 根据imageview宽高 decode 图片
         */
        public static Bitmap decodeStream(InputStream is, ImageView imageView) {
            Bitmap bitmap = null;
            BufferedInputStream bis = null;
            ByteArrayOutputStream baos = null;
    
            try {
                BitmapFactory.Options opts = new BitmapFactory.Options();
                opts.inJustDecodeBounds = true;
    
                bis = new BufferedInputStream(is);
                baos = new ByteArrayOutputStream();
    
                byte buffer[] = new byte[1024];
                int len;
                while ((len = bis.read(buffer, 0, buffer.length)) > 0) {
                    baos.write(buffer, 0, len);
                }
    
                byte[] imageData = baos.toByteArray();
    
                BitmapFactory.decodeByteArray(imageData, 0, imageData.length, opts);
    
                //获取imageview想要显示的宽和高
                ImageSize imageViewSize = getImageViewSize(imageView);
                opts.inSampleSize = ImageUtils.calculateInSampleSize(opts,
                        imageViewSize.getWidth(), imageViewSize.getHeight());
    
                opts.inJustDecodeBounds = false;
                bitmap = BitmapFactory.decodeByteArray(imageData, 0, imageData.length, opts);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                IOUtils.close(bis);
                IOUtils.close(baos);
                IOUtils.close(is);
            }
            return bitmap;
        }
    

    网上搜了好多也没找到具体原因,只能看源代码了:

    if (!is.markSupported()) {  
        is = new BufferedInputStream(is, DECODE_BUFFER_SIZE);  
    }  
    // so we can call reset() if a given codec gives up after reading up to  
    // this many bytes. FIXME: need to find out from the codecs what this  
    // value should be.  
    is.mark(1024);  
    

    于是看明白了,第一次取图片尺寸的时候is这个InputStream被使用过了,再真正取图片的时候又使用了这个InputStream,此时流的起始位置已经被移动过了,需要调用is.reset()来重置,然后再decodeStream(imgInputStream, null, options)就没问题了。 但是注意一个问题,is.mark(1024)是SDK中写死的,如果图片的大小超过1024字节,第一次decode取尺寸之后调用is.reset()会抛出IOException

    http://stackoverflow.com/questions/10240042/ioexception-cannot-load-file

    相关文章

      网友评论

          本文标题:关于BitmapFactory.decode

          本文链接:https://www.haomeiwen.com/subject/ortchttx.html