参考链接
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