1.高效加载图片
加载图片主要用到BitmapFactory类的四个方法:decodeFile()从文件中加载,decodeResource()从资源文件中加载,decodeStream()从输入流,decodeByteArray()从自己数组中加载,
decodefile和decodeResource调用decodeStream实现,四个方法底层都是有native方法实现的。
高效加载图片思想:根据imageView的大小对图片进行压缩,减少内存使用。
压缩图片使用BitmapFactory.Options类的inSampleSize参数(采样率),该参数指将图片的宽高的像素各除SampleSize,inSampleSize总是2的倍数,系统会自行处理,小1取1。
获取到了采样率之后就可以使用BitmapFactory进行加载了。
加载流程:
1.BitmapFactory.Options的inJustDecodeBounds设为true,只对图片的宽高信息进行解析,并将宽高保存在options内,不会对图片主体进行解析。获取的宽高信息与图片所在目录和手机屏幕的密度相关,如果手机屏幕密度大于图片所在Drawable文件夹对应的屏幕密度,那么图片会被放大,反之会被缩小。
2.使用获得的宽高信息和需要的View的宽高,计算得到采样率。
3.再次使用BitmapFactory加载图片,但是将Options.inJustDecodeBounds 设为false,options使用前面得到的,就可以将图片进行压缩了。
以上步骤对于decodeStream来说可能存在问题,因为decodeStream两次操作会改变文件流的位置属性,导致第二次decodeStream时得到的是null,可以通过文件流的文件描述符来加载图片,BitMapFactory.decodeFileDescriptor(),lrudiskcache就是使用的该方式从文件流中加载。
LRU缓存策略
缓存策略主要指,缓存的添加,删除和获取的三个方法对应的算法,即如何将旧的删除新的添加的策略。
LRU,最近最少使用策略,思想:当缓存满时将最近最少使用的对象移除。
LRUCache是个泛型类,内部使用LinkedHashMap以强引用的方式缓存对象。
强引用:平时常用的引用方式。
软引用:当对象只有软引用存在时,系统在内存不足时会将该对象回收掉。
弱引用:当对象只有弱引用时,系统只要gc时就会将该对象回收。
来看看lrucache的源码。
private final LinkedHashMap<K, V> map;
/** Size of this cache in units. Not necessarily the number of elements. */
private int size;
private int maxSize;
private int putCount;
private int createCount;
private int evictionCount;
private int hitCount;
private int missCount;
/**
* @param maxSize for caches that do not override {@link #sizeOf}, this is
* the maximum number of entries in the cache. For all other caches,
* this is the maximum sum of the sizes of the entries in this cache.
*/
public LruCache(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
this.maxSize = maxSize;
this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
}
成员包含了一些统计数据,构造函数创建了一个hashmap指定加载因子为0.75
看看get方法
/**
* Returns the value for {@code key} if it exists in the cache or can be
* created by {@code #create}. If a value was returned, it is moved to the
* head of the queue. This returns null if a value is not cached and cannot
* be created.
*/
public final V get(K key) {
if (key == null) {
throw new NullPointerException("key == null");
}
V mapValue;
synchronized (this) {
mapValue = map.get(key);
if (mapValue != null) {
hitCount++;
return mapValue;
}
missCount++;
}
/*
* Attempt to create a value. This may take a long time, and the map
* may be different when create() returns. If a conflicting value was
* added to the map while create() was working, we leave that value in
* the map and release the created value.
*/
V createdValue = create(key);
if (createdValue == null) {
return null;
}
synchronized (this) {
createCount++;
mapValue = map.put(key, createdValue);
if (mapValue != null) {
// There was a conflict so undo that last put
map.put(key, mapValue);
} else {
size += safeSizeOf(key, createdValue);
}
}
if (mapValue != null) {
entryRemoved(false, key, createdValue, mapValue);
return mapValue;
} else {
trimToSize(maxSize);
return createdValue;
}
}
看到lrucache是线程同步的,同理put方法也是
/**
* Caches {@code value} for {@code key}. The value is moved to the head of
* the queue.
*
* @return the previous value mapped by {@code key}.
*/
public final V put(K key, V value) {
if (key == null || value == null) {
throw new NullPointerException("key == null || value == null");
}
V previous;
synchronized (this) {
putCount++;
size += safeSizeOf(key, value);
previous = map.put(key, value);
if (previous != null) {
size -= safeSizeOf(key, previous);
}
}
if (previous != null) {
entryRemoved(false, key, previous, value);
}
trimToSize(maxSize);
return previous;
}
使用时,我们在创建lrucache对象时,需要复写其sizeof方法,写出要缓存的对象的内存计算方法将结果返回,列如缓存图片可以这样写:
int sizeof(String key,Bitmap bitmap){
return bitmap.getRowBytes()*bitmap.getHeight()/1024;
}
DiskLRUCache
将缓存对象写入文件系统实现缓存效果。
网络图片加载的思路:
加载图片步骤:内存缓存,disk缓存,网络下载
本地没有图片的加载图片步骤:下载图片httpurlconnection获得Stream 存入disklrucache,对图片进行高效加载(即压缩),同时加入到内存lrucache,显示到imageview上。
同步和异步加载
同步加载是指由调用者自行创建线程实现不在ui线程中加载图片,可以在内部判断不能再主线程执行
异步加载使用线程池和handler(使用mainlooper),内存缓存没有情况下,将加载图片操作在子线程执行,使用runnabe创建对象,用线程池excutor执行。也可以使用asynctask,但不能实现多线程并发(在3.0以后asyncTask为单线程的线程池)。需要解决图片错位的问题,给imageview设置tag,当tag和url不同时,不给imageview设置图片。
优化列表滑动卡顿:滑动时不加载图片减少线程创建,getView中不进行耗时操作,开启硬件加速