參考鏈接
http://bbs.itcast.cn/thread-87019-1-1.html
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/0731/1639.html
1 Picasso介紹
picasso是Square公司開源的一個Android圖形緩存庫,地址http://square.github.io/picasso/,可以實現圖片下載和緩存功能。github地址是https://github.com/square/picasso。
Picasso不僅實現了圖片異步加載的功能,還解決了android中加載圖片時需要解決的一些常見問題:
1.在adapter中需要取消已經不在視野範圍的ImageView圖片資源的加載,否則會導致圖片錯位,Picasso已經解決了這個問題。
2.使用複雜的圖片壓縮轉換來儘可能的減少內存消耗
3.自帶內存和硬盤二級緩存功能
Android系統作爲圖片資源加載的主角,它是通過圖像的像素點來把圖像加載到內存中的;現在一張500W的攝像頭拍出的照片(2592x1936),加載到內存中需要大約19M的內存;如果你加入了信號強度不一的網絡中進行了複雜的網絡請求,並進行圖片的緩存與其他處理,你會耗費大量的時間與精力來處理這些問題,但如果用了Picasso, 這些問題都一消而散;
2 Picasso的使用
(1)使用前
在Android studio中使用Picasso,在Gradle中添加
compile 'com.squareup.picasso:picasso:2.5.2'
如果使用ProGuard打包的話,需要將以下代碼添加到混淆規則文件中::
-dontwarn com.squareup.okhttp.**
(2)從網絡加載一張圖片
Picasso使用簡單易用的接口,並有一個實現類Picasso,一個完整的功能請求至少需要三個參數:
with(Context context) - Context上下文在很多Android Api中都是必須的
load(String imageUrl) - 圖片網絡加載地址
into(ImageView targetImageView) - 想進行圖片展示的ImageView
ImageView targetImageView = (ImageView) findViewById(R.id.imageView);
String internetUrl = "http://www.jycoder.com/json/Image/1.jpg";
Picasso
.with(context)
.load(internetUrl)
.into(targetImageView);
(3)從Android Resources中加載
代碼也是三行,只需要將網絡資源地址更改爲一個int值地址即可,上代碼:
ImageView targetImageView = (ImageView) findViewById(R.id.imageView);
int resourceId = R.mipmap.ic_launcher;
Picasso
.with(context)
.load(resourceId)
.into(targetImageView);
(4)從本地File文件中加載
如果你讓用戶選擇本地的一張圖片進行展示的話,就需要用到這個加載方式了,當然,也是So Easy,只需要將地址更換爲一個File即可,上代碼:
ImageView targetImageView = (ImageView) findViewById(R.id.imageView);
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "Running.jpg");
Picasso
.with(context)
.load(file)
.into(targetImageView);
3 LruCache分析
Lrucacha,存儲的結構採用了LinkedHashMap,這種map內部實現了lru算法(Least Recently Used 近期最少使用算法)。
this.map = new LinkedHashMap<String, Bitmap>(0, 0.75f, true);
其中第三個參數的意思是:
-true : 按照訪問的循序排序
-false: 按照插入的順序排序
Lru算法是最近最少使用,需要按照訪問的順序來排序,所說義設爲true
主要是get和set方法:
get()方法
@Override public Bitmap get(String key) {
if (key == null) {
throw new NullPointerException("key == null");
}
Bitmap mapValue;
synchronized (this) {
mapValue = map.get(key);
if (mapValue != null) {
hitCount++;
return mapValue;
}
missCount++;
}
return null;
}
set方法
@Override public void set(String key, Bitmap bitmap) {
if (key == null || bitmap == null) {
throw new NullPointerException("key == null || bitmap == null");
}
Bitmap previous;
synchronized (this) {
putCount++;
size += Utils.getBitmapBytes(bitmap);
previous = map.put(key, bitmap);
if (previous != null) {
size -= Utils.getBitmapBytes(previous);
}
}
trimToSize(maxSize);
}
因爲可能會涉及多線程,所以在存取的時候都會加鎖。而且每次set操作後都會判斷當前緩存區是否已滿,如果滿了就清掉最少使用的圖形。代碼如下:
private void trimToSize(int maxSize) {
while (true) {
String key;
Bitmap value;
synchronized (this) {
if (size < 0 || (map.isEmpty() && size != 0)) {
throw new IllegalStateException(getClass().getName()
+ ".sizeOf() is reporting inconsistent results!");
}
if (size <= maxSize || map.isEmpty()) {
break;
}
Map.Entry<String, Bitmap> toEvict = map.entrySet().iterator()
.next();
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);
size -= Utils.getBitmapBytes(value);
evictionCount++;
}
}
}
4 遇到的問題及解決方案
我在項目中,使用內部類Target的對象作爲into()的參數時,發現第一次總是無法獲取頭像,即不回調onBitmapLoaded()方法而是回調了onPrepareLoad()方法。在stackoverflow中得知Picasso對Target是弱引用,當發生內存回收時Target對象被回收舊無法完成加載圖像。解決的方法是將Target對象設置爲View的Tag,再通過view的getTag()方法來得到Target的對象。
http://stackoverflow.com/questions/24180805/onbitmaploaded-of-target-object-not-called-on-first-load