首先,我们看一下效果图:
图一和图二所展示的图片均为缩略图,我们对于其实现方式分别进行分析。
一、LruCache类:
LruCache类是一个缓存的策略类,简单理解就是它会维持一个对列,有一个上限,当超过上限的时候,会优先清除掉最近最少使用的内容,以维持一个合理的缓存,我们这里主要对bitmap进行缓存处理。
使用:
初始化cache之前要确定当前可以使用的大小,官方给出的默认值为当前可用的cache的1/8,所以我们在初始化的时候要定义一下cache的大小,代码如下:
int maxMemory = (int) Runtime.getRuntime().maxMemory();//获得运行的内存大小
int cacheSize = maxMemory / 8;
mCache = new LruCache<String, Drawable>(cacheSize){//我们这里对图片处理
@Override
protected int sizeOf(String key, Drawable value) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) value;
Bitmap bitmap = bitmapDrawable.getBitmap();
return bitmap.getByteCount();
}
};
这里sizeOf返回的是键值对的条目大小,壁纸这里返回的是当前设置的drawable的bitmap大小;作为每个item的大小规格。
而且在具体使用的时候,我们的key为每张图片的path,value是每张图片的drawable对象。
LurCache的实现原理:
逻辑是维护一个缓存对象列表,对其中对象的排列方式按照访问顺序实现,一直没有访问的放在队尾,最先被淘汰;而最近访问的放在队头,最后被淘汰。它所依赖的对列是一个LinkedHashMap,排列方式按照<key, value>进行;LinkedHashMap是一个双向链表的结构。
二、AsyncTask类:
AsyncTask是一个Handler的包装类,主要通过Thread和Handler来实现多线程的通信,在子线程中执行耗时操作,然后在执行完成之后通过Handler发送Message消息给主线程(UI线程)来更新UI。而AsyncTask好用之处是它已经提供给了我们需要的各个时期的方法,我们可以直接拿来使用,非常方便,避免手动处理异步线程带来的问题。
1.AsyncTask的泛型参数:
Params:在执行AsyncTask时传入的参数,用于后台任务中的使用。
Progress:后台任务执行的时候,在界面上显示的进度。
Result:任务执行完毕之后,需要对结果进行返回,这里是执行返回值的类型。
2.几个方法:
onPreExecute():这个方法在后台任务执行之前调用,用于对界面进行一些初始化操作。
doInBackground(Params...):这个是启动一个子线程,在其中执行耗时操作,任务一旦完成,通过return语句来讲任务的执行结果返回;如果执行的Result为void,那么就不会返回任务的执行结果。他不会直接对UI进行调用,会调用publishProgress(Progress...)来完成。
onProgressUpdate(Progress...):当调用了publishProgress(Progress...)方法后,publishProgress方法会调用该方法,从而对UI进行操作。
onPostExecute(Result):当后台任务执行完毕并且通过Result参数返回时,调用该方法,然后利用返回的数据进行一些UI操作。
3.具体使用:
我们这里定义一个AsyncLoadImageTask类来对Bitmap进行异步加载,代码如下:
public class AsyncLoadImageTask extends AsyncTask<Integer, Void, Drawable>{//因为要显示bitmap缩略图,所以返回的是一个drawable对象
private String url = null;
private final WeakReference<ImageView> mImageViewWeakReference;
private ImageView mImageView;
public AsyncLoadImageTask(ImageView imageView){//传入的是ImageView
this.mImageView = imageView;
mImageViewWeakReference = new WeakReference<ImageView>(imageView);
}
@Override
protected Drawable doInBackground(Integer... integers) {//这里主要是对缩略图的压缩操作
Drawable drawable = null;
if (isCancelled()){
return null;
}
if (integers[0] >= mList.size()){
this.cancel(true);
}else {
Constants.debug("integers[0] : " + integers[0]);
this.url = mList.get(integers[0]).getPath();
drawable = getBitmapFromUrl(this.url);
Constants.mDList.add(integers[0], drawable);
if (drawable != null){
mCache.put(this.url, drawable);
}
}
return drawable;
}
@Override
protected void onPostExecute(Drawable drawable) {//这里执行缩略图的返回值,更新UI
if (isCancelled()){
drawable = null;
}
if (mImageViewWeakReference != null){
if (drawable != null && mImageView != null){
mImageView.setImageDrawable(drawable);//这里更新UI的具体代码
}
}
super.onPostExecute(drawable);
}
}
我们这里用到了WeakReference这个java类,他的主要作用是建立一种弱连接,以防止内存泄漏的出现。我们这里将当前的imageView建立一个弱连接,如果他本身为空的时候被GC掉,那么依赖他的方法体或者对象会自动判断是否为null,从而避免了内存泄漏情况的出现。【可以参考这个博客的内容】
三、Bitmap的压缩策略
在加载的时候,我们先获得FileInputStream对象,然后用FileDescriptor获得FD,getFD返回的是File输入流,在这基础上我们使用BitmapFactory.Options的decodeFileDescription获得bitmap对象。
代码如下:
private static Bitmap decodeSampleFromFilePath(String filePath, int reqWidth, int reqHeight){
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(filePath);//fileInputStream对象
}catch (Exception e){
e.printStackTrace();
}
FileDescriptor fileDescriptor = null;
if (fileInputStream == null){
return null;
}
try {
fileDescriptor = fileInputStream.getFD();//FD
}catch (Exception e){
e.printStackTrace();
}
final BitmapFactory.Options options = new BitmapFactory.Options();
Bitmap bitmap = null;
try {
options.inJustDecodeBounds = true;//设置为true,返回的是图片本身的宽高信息,而且设置为true的时候是不会加载到内存中,不占用内存
BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
options.inSampleSize = calculateRatioSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
}catch (Exception e){
e.printStackTrace();
options.inSampleSize += 1;
options.inJustDecodeBounds = false;
options.inPreferredConfig = Bitmap.Config.RGB_565;//支持的颜色
bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
}
return bitmap;
}
计算需要的比例大小:
private static int calculateRatioSize(BitmapFactory.Options options, int reqWidth, int reqHeight){
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth){
final int halfHeight = height / 2;
final int halfWidth = width / 2;
while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth){
inSampleSize *= 2;
}
}
return inSampleSize;
}
原来觉得bitmap这一块很难懂,其实静下来好好分析一下,也不是特别难~