Fresco内部诟病引起的初次从网络加载PNG图片失败

如题描述,这个问题在项目中存在已久,今天由于自己的功能在首页,初次启动的体验极其糟糕,所以硬下头皮把这个问题解决了。

先来描述一下怎么样一个差的体验吧。就是当我第一次加载网络PNG(当时并不知道是由于这个原因引起的)的时候,就一直显示的是默认的图片,本来按照Fresco的机制是,先显示默认图片,然后去网络加载图片,当图片下载下来就会替换掉原来的默认图片。

感谢https://github.com/facebook/fresco/issues/1803这篇文章让我有了思路。

其实在此之前我已经定位到错误了,我在加载图片的回调里面打了日志,发现没加载出来的图片都走了onFailure方法,抛出了一个异常

IllegalArgumentException Problem decoding into existing bitmap

这个异常是在BitmapFactory.java里面抛出来的,有四个地方抛出,分别是不同的decodeResource,decodeByteArray,decodeStream,decodeFileDescriptor这几个方法抛出的,我们看看其中一个decodeStream吧

public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
        // we don't throw in this case, thus allowing the caller to only check
        // the cache, and not force the image to be decoded.
        if (is == null) {
            return null;
        }

        Bitmap bm = null;

        Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap");
        try {
            if (is instanceof AssetManager.AssetInputStream) {
                final long asset = ((AssetManager.AssetInputStream) is).getNativeAsset();
                bm = nativeDecodeAsset(asset, outPadding, opts);
            } else {
                bm = decodeStreamInternal(is, outPadding, opts);
            }

            if (bm == null && opts != null && opts.inBitmap != null) {
                throw new IllegalArgumentException("Problem decoding into existing bitmap");
            }

            setDensityFromOptions(bm, opts);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
        }

        return bm;
    }

可以看到是在转换bitmap的时候抛出来的。
结合那篇文章可以得出结论是,Fresco在网络加载PNG的时候有一个BUG导致流解析为bitmap的时候抛出异常。

那就解决问题吧。这边我说一下我的解决思路。

第一种方案,就是链接里面说的修改Fresco的源码。可以翻到1.3.0版本的432行,我这里给你找出来了。

public class DecodeProducer implements Producer<CloseableReference<CloseableImage>> {
    //......
     @Override
        protected synchronized boolean updateDecodeJob(EncodedImage encodedImage, boolean isLast) {
          boolean ret = super.updateDecodeJob(encodedImage, isLast);
          if (!isLast && EncodedImage.isValid(encodedImage) &&
              encodedImage.getImageFormat() == DefaultImageFormats.JPEG) {
            if (!mProgressiveJpegParser.parseMoreData(encodedImage)) {
              return false;
            }
            int scanNum = mProgressiveJpegParser.getBestScanNumber();
            if (scanNum <= mLastScheduledScanNumber) {
              // We have already decoded this scan, no need to do so again
              return false;
            }
            if (scanNum < mProgressiveJpegConfig.getNextScanNumberToDecode(mLastScheduledScanNumber)
                && !mProgressiveJpegParser.isEndMarkerRead()) {
              // We have not reached the minimum scan set by the configuration and there
              // are still more scans to be read (the end marker is not reached)
              return false;
            }
            mLastScheduledScanNumber = scanNum;
          }
          return ret;
        }
        //......
}

把下面这个if里面加上 &&encodedImage.getImageFormat() == DefaultImageFormats.PNG

if (scanNum < mProgressiveJpegConfig.getNextScanNumberToDecode(mLastScheduledScanNumber)
            && !mProgressiveJpegParser.isEndMarkerRead()) {
          // We have not reached the minimum scan set by the configuration and there
          // are still more scans to be read (the end marker is not reached)
          return false;
        }

由于现在已经是1.8.0版本的Fresco了,你可以找一下这个类定位到这个地方改。

第二种方案
用反射修改第一个方案里面的这个方法,这可以不修改Fresco的源码

第三种方案,我就是用这个方案的
由于我们App的图片是存在七牛的所以只要在你的Url的后面加上

"?imageView2/1/format/jpg"

把png通过七牛转成jpg,这样就完美解决了。
不过不可能整个app都是用jpg格式的图片,所以可能还是得需要上面两个方案里面的其中一个。如果你有更好的方案,请告诉我一下哈。开源万岁。

你可能感兴趣的:(android)