本文基於 Picasso 2.71828 版本 。本文不僅講用法,更主要講的是錯誤的用法。而具體的源碼分析在其他文章中。
implementation 'com.squareup.picasso:picasso:2.71828'
基本用法
從 assets 中加載
Picasso.get()
.load("file:///android_asset/test_img")
.into(imageView);
從資源 Id 中加載圖片
方法一 :
Picasso.get()
.load(R.drawable.test_img)
.into(imageView);
方法二 :
Picasso.get()
.load("android.resource://" + getPackageName() + "/" + R.drawable.test_img)
.into(imageView);
從 SD 中加載文件
File externalFile = getExternalFilesDir(null).getAbsoluteFile();
File targetFile = new File(externalFile, "test.jpg");
Picasso.get()
.load(targetFile)
.into(imageView);
從網絡中加載圖片
Picasso.get()
.load("http://i.imgur.com/DvpvklR.png")
.into(imageView);
設置占位圖
Picasso.get()
.load("http://i.imgur.com/DvpvklR.png")
.placeholder(R.drawable.placeHolder)
.into(imageView);
設置加載失敗圖片
Picasso.get()
.load("http://i.imgur.com/DvpvklR.png")
.error(R.drawable.error_image)
.into(imageView);
加載指定大小的圖片 :
例如 : http://i.imgur.com/DvpvklR.png 圖片分辨率為 400* 486 ,Bitmap.config 為 ARGB_8888 , 即每個像素占 4 個字節。
Picasso.get()
.load("http://i.imgur.com/DvpvklR.png")
.resize(200,200)
.into(imageView);
如果不調用 resize() 方法 , 圖片所占用內存大小為 400 * 486 * 4 個字節 。
調用了 resize() 方法后,圖片所占用內存大小為 200 * 200 * 4 個字節。
可以發現內存占用減少了很多。
但是請務必注意 , 千萬不要以為有了圖片加載框架,就可以為所欲為 !!!
resize() 不僅可以縮小,同時也會放大圖片。下面代碼 resize()之后 加載了 3500 * 3500 分辨率的圖片到內存中。
所占用內存為 3500 * 3500 * 4 /(1024*1024) = 46.7 M 。如果你的頁面存在內存泄露 , 嘿嘿 ,等着加班修 Bug 吧 !
Picasso.get()
.load("http://i.imgur.com/DvpvklR.png")
.resize(3500,3500)
.into(imageView);
因此使用 resize 的時候,最好加上 onlyScaleDown() 方法。
只縮小不放大, 當 resize 尺寸大於 Bitmap 尺寸的時候 ,不放大圖片。
當 resize 尺寸小於 Bitmap 尺寸的時候,縮小圖片。
下面代碼最終加載了 400 * 486 分辨率的圖片到內存中 。
Picasso.get()
.load("http://i.imgur.com/DvpvklR.png")
.resize(3500,3500)
.onlyScaleDown()
.into(imageView);
設置圖片 ScaleType
以下寫法全是錯誤寫法 :
Picasso.get().load("http://i.imgur.com/DvpvklR.png").centerCrop().into(imageView);
Picasso.get().load("http://i.imgur.com/DvpvklR.png").centerInside().into(imageView);
Picasso.get()
.centerInside()
.centerCrop()
.into(imageView);
centerCrop() 和 centerInside() 單獨使用的時候, 必須調用 resize()方法。
centerCrop() 和 centerInside() 不能同時使用。
取消加載圖片
當我們退出頁面 Activity/fragment 的時候,不想再加載圖片,我們需要取消請求。
Picasso.get().cancelRequest(imageView);
// 或
Picasso.get().cancelTag(tag);
這里大家要注意一下問題 :
如果正在下載一張圖片,你取消了請求,有用嗎 ?
答案: 網絡請求不會中斷,仍會占用網絡繼續下載,但是 ImageView 不會收到回調。
這里的 “正在下載” 的意思是 :
線程是正在執行 Runnable 方法 。 如果請求在線程池的等待隊列當中,那么不會下載 。如果正在執行,則不會中斷。
暫停加載圖片
大家看到暫停加載圖片,不要以為是真的支持斷點下載。
這里的暫停加載圖片,實際上是取消下載圖片。並把當前請求添加到暫停列表中。
Picasso.get().pauseTag(tag);
繼續加載圖片
這里的繼續加載圖片,是重新把請求交給 Picasso 來下載,也不是斷點下載。
Picasso.get().resumeTag(tag);
這個 暫停和繼續 下載圖片有什么用呢 ? 大多數情況下是用在 ListView/ RecyclerView 當中。
當快速滑動的時候暫停下載圖片,當滑動停止的時候繼續加載圖片。
跳過內存緩存加載圖片
有時候不想從內存緩存中加載圖片,而想直接從磁盤或網絡中加載圖片。
我們可以設置其內存策略為 MemoryPolicy.NO_CACHE 。
Picasso.get()
.load("http://i.imgur.com/DvpvklR.png")
.memoryPolicy(MemoryPolicy.NO_CACHE)
.into(imageView);
加載的圖片不存放到內存緩存中
例如我提前知道一張圖片分辨率很大,占用的內存很多,因此我不想把這張圖片加載到內存中。
我們可以設置它的內存策略為 MemoryPolicy.NO_STORE 。
Picasso.get()
.load("http://i.imgur.com/DvpvklR.png")
.memoryPolicy(MemoryPolicy.NO_STORE)
.into(imageView);
圖片加載回調
注意 ,下面的寫法會造成內存泄露 !
Picasso.get().load("http://i.imgur.com/DvpvklR.png").into(imageView, new Callback() {
@Override
public void onSuccess() {
}
@Override
public void onError(Exception e) {
}
});
Callback 匿名內部類持有外部類的引用 。因此在 Activity/Fragment 銷毀的時候注意取消請求。
Picasso.get().cancelRequest(imageView);
同時注意到, 上邊的回調,並沒有 Bitmap 對象。 有時候我們需要拿到 Bitmap 對象來做些額外的處理 ,見下:
圖片加載 Bitmap 回調
注意,下面的寫法也是錯誤的寫法
Picasso.get().load("http://i.imgur.com/DvpvklR.png").into(new Target() {
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
}
@Override
public void onBitmapFailed(Exception e, Drawable errorDrawable) {
}
@Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
});
在 Picasso 中持有的 ImageView / Target 都是弱引用 , 上面的代碼意味着 , Target 隨時可能被回收 , 這些回調可能不會被調用。
正確寫法如下 : 將 Target 聲明為成員變量, Activity/fragent 銷毀的時候要取消請求。
Picasso.get()
.load("http://i.imgur.com/DvpvklR.png")
.into(target);
...
private Target target = new Target() {
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
}
@Override
public void onBitmapFailed(Exception e, Drawable errorDrawable) {
}
@Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
};
....
@Override
protected void onDestroy() {
super.onDestroy();
Picasso.get().cancelRequest(target);
}
預加載圖片到內存當中
有的時候,為了更快的拿到圖片,我們需要提前從網絡或磁盤中加載圖片到內存中。
例如有兩個頁面 A、B ,在 A 頁面的時候就去下載一張圖片,跳轉到 B 頁面的時候立即顯示圖片,這樣體驗更好。
因此在 A 頁面的時候,我們就可以調用。
Picasso.get()
.load("http://i.imgur.com/DvpvklR.png")
.fetch();
如果你需要回調 fetch 方法的回調 : 下面代碼同樣這是錯誤的寫法 ,理由同上,匿名內部類持有外部類造成的內存泄露 。但是這里 fetch 又不能直接取消。
這里有兩個解決方法。
1. 設置 tag , 在 Acttivity /fragment 銷毀的時候,取消掉 tag 。
2. 使用 靜態內部類 , 如果需要引用 Acttivity /fragment ,請使用弱引用來避免內存泄露。
3. 不使用 fetch() 方法,使用上述的 into(Target) 方法 。
Picasso.get().load("http://i.imgur.com/DvpvklR.png").fetch(new Callback() {
@Override
public void onSuccess() {
}
@Override
public void onError(Exception e) {
}
});
同步方法獲取 Bitmap
上面獲取 Bitmap 對象的方法都是異步方法 ,如果需要同步方法 ,可以調用 get 方法 :
try {
Bitmap bitmap = Picasso.get().load("http://i.imgur.com/DvpvklR.png").get();
} catch (IOException e) {
e.printStackTrace();
}
使用時請務必注意一下兩點
1.get() 方法獲取到 Bitmap 后 ,沒有存放到內存緩存 ( LruCache )中 。
原因是 Picasso 不能夠保證 Cache 的實現都是線程安全的。
大家注意一下, Picassso 中的 LruCache 實現了 Cache 接口 , 內部實際調用了 android.util 包下的 LruCache 。
而 android 的 LruCache 是線程安全的。
從代碼中也可以看出 Picasso 中的 LruCache 方法也是線程安全的。
而 Picasso 是了為了防止用戶 自定義 Cache , 沒有保證 set() 和 get() 方法的線程安全。因此才不放到內存緩存當中。
2. get() 方法不能在主線程調用 ,必須在異步線程中調用 。get() 方法是同步方法 ,會從磁盤或網絡中加載圖片,是耗時操作。
