例如圖片網址: http://upload.wikimedia.org/wikipedia/commons/thumb/7/72/Flag_of_the_Republic_of_China.svg/125px-Flag_of_the_Republic_of_China.svg.png
Before:
new AsyncTask<String, Void, Bitmap> {
@Override
protected Bitmap doInBackground(String... urls) {
String url = urls[0];
Bitmap bitmap = null;
try {
InputStream in = new java.net.URL(url).openStream();
bitmap = BitmapFactory.decodeStream(in);
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap result) {
if (result != null) mImageView.setImageBitmap(result);
}
}.execute("http://upload.wikimedia.org/wikipedia/commons/thumb/7/72/Flag_of_the_Republic_of_China.svg/125px-Flag_of_the_Republic_of_China.svg.png");
After:
AUIL(Android-Universal-Image-Loader):
ImageLoader.getInstance().displayImage("http://upload.wikimedia.org/wikipedia/commons/thumb/7/72/Flag_of_the_Republic_of_China.svg/125px-Flag_of_the_Republic_of_China.svg.png", mImageView);
Picasso:
Picasso.with(context).load("http://upload.wikimedia.org/wikipedia/commons/thumb/7/72/Flag_of_the_Republic_of_China.svg/125px-Flag_of_the_Republic_of_China.svg.png").into(mImageView);
Glide:
Glide.with(context).load("http://upload.wikimedia.org/wikipedia/commons/thumb/7/72/Flag_of_the_Republic_of_China.svg/125px-Flag_of_the_Republic_of_China.svg.png").into(mImageView);
Fresco:
mImageView.setImageURI(Uri.parse("http://upload.wikimedia.org/wikipedia/commons/thumb/7/72/Flag_of_the_Republic_of_China.svg/125px-Flag_of_the_Republic_of_China.svg.png"));
除了寫法的簡便之外,為什麼要 Image Loader ,什麼是 Image Loader ?
ImageLoader 會很聰明的知道 ImageView 在可視範圍內才去做下載與解壓縮 bitmap 一旦離開就停止一切作業。而且為了二次快速顯示,依據 ImageView 的可視長寬作參考最適 cache 。
- 顯示快取 - 首先,為了再次顯示一樣的圖片時,可以快速顯示,所以我們會把 bitmap 暫存在記憶體 memory cache 。那在有限的 memory cache ,我們要如何管理 ? 常見的是 LRU cache 策略,memory cache 有限,最近看過的優先留下來。以及依據畫布大小的快取。
- 儲存快取 - 再來,網路來的圖片、網址圖片,下載儲存快取管理 - Disk Cache。
- 連線快取 - okhttp SPDY
在 fresco 出現之前,會比較推崇 AUIL(Android Universal Image Loader),接著 glide 、picasso 。
追求小 code size 可以選 picasso ,輕簡。
AUIL 是因為在記憶體、儲存空間的快取策略還有其它有的沒有的都可以訂製。彈性比較大一點(所以 code size 大一點)。
只有 fresco 需要更換 layout class 原因是因為它為了效能,操作較低階的畫布。(Layout Class 都換了,所以順便支援 Gif 、影片? 誤)
對於 Image Loader 常見的需求:
- 更換 OkHttp 下載器
- 潤角、圓圖、顯示動畫
- 支援 Exif 轉正,支援影片快照轉正
Fresco - facebook
- ImagePipeLine - 更換 OkHttp 下載器 (有 bug ,已解 PR#21)
picasso - square
- Factory -> 更換 OkHttp 下載器
- Transformer -> 潤角、圓圖、顯示動畫
Android-Universal-Image-Loader
- ImageDownloader -> 更換 OkHttp 下載器
- ImageDisplayer -> 潤角、圓圖、顯示動畫
- ImageDecoder -> 支援 Exif 轉正,支援影片轉正
glide 許多 Google 官方 samples 都可看到它的蹤影,所以基本上 Google 有挺。
如果你的 ImageLoader 針對網路圖片,不支援轉址,又或者網址藏在 json 裡。
ImageLoader 大多支援 ContentProvider 網址,content://
所以我們可以利用它來做虛擬網址轉址。
p.s. https://graph.facebook.com/{id}/picture
雖然本身有轉址能力,不過這裡為了教學所需,還是寫了一個 FacebookPictureProvider 作本地轉址範例。
例如:
GET https://graph.facebook.com/601234567/picture?redirect=0
:
{
"data": {
"is_silhouette": false,
"url": "https://fbcdn-profile-a.akamaihd.net/hprofile-ak-xpf1/v/t1.0-1/p50x50/1234567"
}
}
data.url 才是真實的圖片網址。
所以我們註冊一個內部網域:
content://com.facebook.content.PictureProvider/601234567
提供轉址到真實圖片網址:
https://fbcdn-profile-a.akamaihd.net/hprofile-ak-xpf1/v/t1.0-1/p50x50/1234567
public class FacebookPictureProvider extends NetworkPipeContentProvider { // NetworkPipeContentProvider 是筆者包裝過的,可參下方 github
public static final String AUTHORITY = "com.facebook.provider.PictureProvider";
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/");
private Facebook facebook;
// content://com.facebook.provider.PictureProvider/601234567
@Override
public String getUri(Uri uri) { // 轉址 - 改寫網址
Picture picture = facebook.picture(uri.getPathSegments().get(0)).toBlocking().first();
return picture.data.url;
}
interface Facebook {
// https://graph.facebook.com/{id}/picture
// https://graph.facebook.com/601234567/picture
@GET("/{id}/picture")
Observable<Picture> picture(@Path("id") String id);
}
static class Picture {
Data data;
}
static class Data {
// "https://fbcdn-profile-a.akamaihd.net/hprofile-ak-xpf1/v/t1.0-1/p50x50/1234567"
String url;
}
@Override
public boolean onCreate() {
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("https://graph.facebook.com")
.build();
facebook = restAdapter.create(Facebook.class);
return true;
}
}
- Using okhttp backed for Volley - https://gist.github.com/bryanstern/4e8f1cb5a8e14c202750
- facebook/fresco#21