比較大的圖片無法加載
如果你的項(xiàng)目中有以下的代碼,并且使用 2.5.2 (以前有沒有這個(gè)問題我沒有去驗(yàn)證),很可能會(huì)出現(xiàn)較大的圖片無法加載
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 以后的代碼會(huì)修復(fù)這個(gè)BUG(然而Picasso已經(jīng)N年不更新了,據(jù)說打算release 3.0.0)
那么這個(gè)問題為何會(huì)出現(xiàn)呢? 這里需要通過下面的代碼拿到 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();
當(dāng)圖片加載無法加載的時(shí)候會(huì)有這樣一條log

這個(gè)異常是由 MarkableInputStream.java 拋出,對(duì)應(yīng)的代碼如下
public void reset(long token) throws IOException {
if (offset > limit || token < reset) {
throw new IOException("Cannot reset");
}
in.reset();
skip(reset, token);
offset = token;
}
其中l(wèi)imit的大小是65536,而offset是表示讀取流的位置,在 BitmapHunter.java 中調(diào)用了 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);
...
}
這里是圖片加載比較常見的操作,先計(jì)算bitmap的options,然后才會(huì)真正的將圖片加載到內(nèi)存中。在獲取正確的options之后,Picasso會(huì)將整個(gè)流reset,這個(gè)時(shí)候如果offset大于limit就會(huì)拋出異常,導(dǎo)致圖片加載失敗。
Picasso在 2.5.2 以后使用了動(dòng)態(tài)調(diào)整limit的方式,增長(zhǎng)limit大小,從而解決了這個(gè)問題
Reference:
Added dynamic limit option to MarkableInputStream
java.io.IOException: Cannot reset (Huge photo loading)
首次加載的時(shí)候圖片無法顯示
如果你的代碼是下面這種寫法,那么恭喜你,這個(gè)問題也是可以解決的
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 通過強(qiáng)引用(比如類的屬性或者M(jìn)ap之類)保存起來,例如
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));
}
}
這個(gè)問題產(chǎn)生的原因是target在Picasso內(nèi)部是通過弱引用保存的,就會(huì)導(dǎo)致target很容易被GC清理,這時(shí)候 onBitmapLoaded 就不會(huì)被回調(diào)了
Reference:
Target.onBitmapLoaded() method not called sometimes