总结一下微信的本地图片加载有以下几个特点,也是提高用户体验的关键点
1、缩略图挨个加载,一个一个加载完毕,直到屏幕所有缩略图都加载完成
2、不等当前屏的所有缩略图加载完,迅速向下滑,滑动停止时立即加载停止页面的图片
3、已经加载成功的缩略图,不管滑出去多远,滑回来的时候不需要重新加载
4、在相册以外的环境中,需要让imageView的宽高比例随图片的宽高比例自动伸缩,而且要在图片加载完毕之前就要预留占位空间
为了满足上面几个要求,主要采用以下几个方法:
0、为了防止图片加载出来OOM,需要对分辨率和颜色的位数进行缩小到合适范围,同时采用LRU缓存
1、采用一个定长线程池,线程池的大小等于CPU的数量+1,把所有缩略图加载任务都交给线程池执行,以获得最快的加载效率。
2、在用户快速滑动的时候,没有加载完毕的划走了的图片立即停止加载,将所占线程让出来,让新的加载任务执行。
3、已经加载成功的缩略图,保存到sd卡中,下次再滑动回来的时候,直接从sd卡加载以前保存好的小图,不经过线程池。
4、对于三星这样的手机,其图片全都是宽度大于高度,方向用exif进行记录,图片加载器要读出exif的方向信息,然后通过矩阵进行旋转
效果展示:
github项目地址:https://github.com/AlexZhuo/AlxImageLoader
下面就是加载器主要部分的注释和代码
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.util.Log;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.demo.task.AlxMultiTask;
/**
* Created by Administrator on 2016/4/8.
*/
public class AlxImageLoader {
private Context mcontext;
private HashMap<String, SoftReference<Bitmap>> imageCache = new HashMap<String, SoftReference<Bitmap>>();
private ConcurrentHashMap<ImageView,String> currentUrls = new ConcurrentHashMap<>();//记录一个imageView应该显示哪个Url,用于中断子线程
private Bitmap defbitmap;
public AlxImageLoader(Context context) {
this.mcontext = context;
defbitmap = BitmapFactory.decodeResource(mcontext.getResources(), R.drawable.upload_photo4x);//没加载到图片的默认显示
}
/**
* 从本地加载一张图片并使用imageView进行显示,可以设置是否根据图片的大小动态修改imageView的高度,宽度必须传入来控制显示图片的清晰度防止oom
* @param uri
* @param imageView
* @param imageViewWidth
* @param resizeImageView
* @param autoRotate
* @param imageCallback
* @return
*/
private Bitmap loadBitmapFromSD(final String uri, final ImageView imageView, final int imageViewWidth, final boolean resizeImageView, final boolean autoRotate,final boolean storeThumbnail ,final ImageCallback imageCallback) {
if (imageCache.containsKey(uri)) {//如果之前已经加载过这个图片,那么就从LRU缓存里加载
SoftReference<Bitmap> SoftReference = imageCache.get(uri);
Bitmap bitmap = SoftReference.get();
if (bitmap != null) {
Log.i("Alex","现在是从LRU中拿出来的bitmap");
return bitmap;//从系统内存里直接拿出来
}
}
final int[] imageSize = {0,0};
if(uri ==null)return null;
if(storeThumbnail) {
File file = new File(imageView.getContext().getCacheDir().getAbsolutePath().concat("/" + new File(uri).getName()));
if (file.exists() && file.length()>1000) {
//因为从file中获取图片的宽高存在IO操作,所以把每个图片的宽高缓存起来
Log.i("Alex", "现在是从