Picasso中一些问题的解决方法

作者: 李冬冬 | 来源:发表于2017-01-05 17:14 被阅读1048次

    比较大的图片无法加载

    如果你的项目中有以下的代码,并且使用 2.5.2 (以前有没有这个问题我没有去验证),很可能会出现较大的图片无法加载

    Picasso.with(context).load(uri)
            .resize(width, height)
            .into(target);
    

    目前的解决办法是将gradle文件中的这一句

    compile 'com.squareup.picasso:picasso:2.5.2'
    

    替换为

    compile 'com.squareup.picasso:picasso:2.6.0-SNAPSHOT'
    

    也就是说在 2.5.2 以后的代码会修复这个BUG(然而Picasso已经N年不更新了,据说打算release 3.0.0)
    那么这个问题为何会出现呢? 这里需要通过下面的代码拿到 Picasso 加载图片失败的日志

    mPicasso = new Picasso.Builder(context.getApplicationContext())
            .listener(new Picasso.Listener() {
                @Override
                public void onImageLoadFailed(Picasso picasso, Uri uri, Exception exception) {
                    Log.e(TAG, uri.toString(), exception);
                }
            }).build();
    

    当图片加载无法加载的时候会有这样一条log

    Picasso加载大图失败log.png

    这个异常是由 MarkableInputStream.java 抛出,对应的代码如下

    public void reset(long token) throws IOException {
      if (offset > limit || token < reset) {
        throw new IOException("Cannot reset");
      }
      in.reset();
      skip(reset, token);
      offset = token;
    }
    

    其中limit的大小是65536,而offset是表示读取流的位置,在 BitmapHunter.java 中调用了 MarkableInputStream.java 中的 reset() 方法,具体代码如下:

    static Bitmap decodeStream(InputStream stream, Request request) throws IOException {
      ...
      final boolean calculateSize = RequestHandler.requiresInSampleSize(options);
      ...
      if (calculateSize) {
        BitmapFactory.decodeStream(stream, null, options);
        RequestHandler.calculateInSampleSize(request.targetWidth, request.targetHeight, options,
            request);
        markStream.reset(mark);
      }
      Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);
      ...
    }
    

    这里是图片加载比较常见的操作,先计算bitmap的options,然后才会真正的将图片加载到内存中。在获取正确的options之后,Picasso会将整个流reset,这个时候如果offset大于limit就会抛出异常,导致图片加载失败。
    Picasso在 2.5.2 以后使用了动态调整limit的方式,增长limit大小,从而解决了这个问题
    Reference:
    Added dynamic limit option to MarkableInputStream
    java.io.IOException: Cannot reset (Huge photo loading)

    首次加载的时候图片无法显示

    如果你的代码是下面这种写法,那么恭喜你,这个问题也是可以解决的

    Picasso.with(context).load(image.getUri())
            .into(new Target() {
                @Override
                public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
                    // your code
                }
    
                @Override
                public void onBitmapFailed(Drawable errorDrawable) {
                    // your code
                }
    
                @Override
                public void onPrepareLoad(Drawable placeHolderDrawable) {
                    // your code
                }
            });
    

    解决方法是把 Target 通过强引用(比如类的属性或者Map之类)保存起来,例如

    class Test {
        private Picasso mPicasso;
        private final Map<String, Target> mTargetMap;
    
        Test(Context context) {
            mPicasso = new Picasso.Builder(context.getApplicationContext())
                    .listener(new Picasso.Listener() {
                        @Override
                        public void onImageLoadFailed(Picasso picasso, Uri uri, Exception exception) {
                            Log.e(TAG, uri.toString(), exception);
                        }
                    }).build();
            mPicasso.setLoggingEnabled(true);
            mTargetMap = new HashMap<>();
        }
    
        public void loadBitmap() {
    
            Target target = new Target() {
                @Override
                public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
                }
    
                @Override
                public void onBitmapFailed(Drawable errorDrawable) {
                }
    
                @Override
                public void onPrepareLoad(Drawable placeHolderDrawable) {
                }
            };
            mTargetMap.put(key, target);
    
            mPicasso.load(uri)
                    .into(mTargetMap.get(key));
        }
    
    }
    

    这个问题产生的原因是target在Picasso内部是通过弱引用保存的,就会导致target很容易被GC清理,这时候 onBitmapLoaded 就不会被回调了
    Reference:
    Target.onBitmapLoaded() method not called sometimes

    相关文章

      网友评论

        本文标题:Picasso中一些问题的解决方法

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