一、以单例方式获得该实例对象
public static ImageLoader getInstance()
{
if (mInstance == null)
{
synchronized (ImageLoader.class)
{
if (mInstance == null)
{
mInstance = new ImageLoader(1, Type.LIFO);
}
}
}
return mInstance;
}
这里利用synchronized 关键字,通过同步锁的方式,进行了线程同步的管理。
关于synchronized 我们进行简单的说明,synchronized 关键字可以用于修饰方法或者代码块上,而不论放在哪里,它取得的锁都是对象,而不是把一段代码或函数当作锁,每个对象只有一个锁(lock)与之相关联。在上述方法中,通过synchronized (ImageLoader.class) 我们可以明确,它锁住的是ImageLoader 类的所有对象。
当然作为单例模式,构造函数的私有化也是必须的,如下:
private ImageLoader(int threadCount, Type type)
{
init(threadCount, type);
}
private void init(int threadCount, Type type)
{
// loop thread
mPoolThread = new Thread()
{
@Override
public void run()
{
Looper.prepare();
mPoolThreadHander = new Handler()
{
@Override
public void handleMessage(Message msg)
{
mThreadPool.execute(getTask());
try
{
mPoolSemaphore.acquire();
} catch (InterruptedException e)
{
}
}
};
// 释放一个信号量
mSemaphore.release();
Looper.loop();
}
};
mPoolThread.start();
// 获取应用程序最大可用内存
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int cacheSize = maxMemory / 8;
mLruCache = new LruCache<String, Bitmap>(cacheSize)
{
@Override
protected int sizeOf(String key, Bitmap value)
{
return value.getRowBytes() * value.getHeight();
};
};
mThreadPool = Executors.newFixedThreadPool(threadCount);
mPoolSemaphore = new Semaphore(threadCount);
mTasks = new LinkedList<Runnable>();
mType = type == null ? Type.LIFO : type;
}
通过上面我们可以知道,在初始化的方法里,我们做了下面几个事情:
1、启动一个线程,利用Handler机制进行任务的轮询。在收到Message后,通过getTask()方法获取当前所要运行的Runnable,添加到线程池。
private synchronized Runnable getTask()
{
if (mType == Type.FIFO)
{
return mTasks.removeFirst();
} else if (mType == Type.LIFO)
{
return mTasks.removeLast();
}
return null;
}
2、获取应用程序的最大内存,定义ImageLoader 可用maxSize(最大内存)为其值得1/8,重写sizeOf 方法,定义缓存中每项的大小,当我们缓存进去一个数据后,当前已缓存的Size就会根据这个方法将当前加进来的数据也加上,便于统计当前使用了多少内存,如果已使用的大小超过maxSize就会进行清除动作。
3、定义一个可控最大并发数为threadCount的定长线程池,超出的线程会在队列中等待。
4、定义一个值为threadCount信号量mPoolSemaphore,用于控制线程池的线程同步。
5、定义任务队列。
6、明确队列的调度方式,枚举Type有LIFO (后进先出)和FIFO (先进先出),默认情况采用LIFO 。
二、查看公共方法loadImage()
public void loadImage(final String path, final ImageView imageView)
{
// set tag
imageView.setTag(path);
// UI线程
if (mHandler == null)
{
mHandler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
ImgBeanHolder holder = (ImgBeanHolder) msg.obj;
ImageView imageView = holder.imageView;
Bitmap bm = holder.bitmap;
String path = holder.path;
if (imageView.getTag().toString().equals(path))
{
imageView.setImageBitmap(bm);
}
}
};
}
Bitmap bm = getBitmapFromLruCache(path);
if (bm != null)
{
ImgBeanHolder holder = new ImgBeanHolder();
holder.bitmap = bm;
holder.imageView = imageView;
holder.path = path;
Message message = Message.obtain();
message.obj = holder;
mHandler.sendMessage(message);
} else
{
addTask(new Runnable()
{
@Override
public void run()
{
ImageSize imageSize = getImageViewWidth(imageView);
int reqWidth = imageSize.width;
int reqHeight = imageSize.height;
Bitmap bm = decodeSampledBitmapFromResource(path, reqWidth,
reqHeight);
int degree=reaPictureDegree(path);
Bitmap rotaingBitmap=rotaingImageView(degree,bm);
addBitmapToLruCache(path, rotaingBitmap);
ImgBeanHolder holder = new ImgBeanHolder();
holder.bitmap = getBitmapFromLruCache(path);
holder.imageView = imageView;
holder.path = path;
Message message = Message.obtain();
message.obj = holder;
mHandler.sendMessage(message);
mPoolSemaphore.release();
}
});
}
}
1、定义Handler用于异步更新ImageView的Bitmap。
2、从LruCache中获取一张图片,如果不存在就返回null。当不为null时,mHandler发送Message;否则,新建一个Runnable加入任务队列,根据ImageView获得适当的压缩的宽和高,对图片进行压缩,把图片存储进内存,mHandler发送Message。
三、具体查看下根据ImageView获得适当的压缩的宽和高的方法,这里不做过多解释,如下:
private ImageSize getImageViewWidth(ImageView imageView)
{
ImageSize imageSize = new ImageSize();
final DisplayMetrics displayMetrics = imageView.getContext()
.getResources().getDisplayMetrics();
final LayoutParams params = imageView.getLayoutParams();
int width = params.width == LayoutParams.WRAP_CONTENT ? 0 : imageView
.getWidth(); // Get actual image width
if (width <= 0)
width = params.width; // Get layout width parameter
if (width <= 0)
width = getImageViewFieldValue(imageView, "mMaxWidth");
if (width <= 0)
width = displayMetrics.widthPixels;
int height = params.height == LayoutParams.WRAP_CONTENT ? 0 : imageView
.getHeight(); // Get actual image height
if (height <= 0)
height = params.height; // Get layout height parameter
if (height <= 0)
height = getImageViewFieldValue(imageView, "mMaxHeight");
if (height <= 0)
height = displayMetrics.heightPixels;
imageSize.width = width;
imageSize.height = height;
return imageSize;
}
四、具体查看下图片压缩的方法,这里不做过多解释,直接看注释即可,如下:
private Bitmap decodeSampledBitmapFromResource(String pathName,
int reqWidth, int reqHeight)
{
// 第一次解析将inJustDecodeBounds设置为true,来获取图片大小
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(pathName, options);
// 调用上面定义的方法计算inSampleSize值
options.inSampleSize = calculateInSampleSize(options, reqWidth,
reqHeight);
// 使用获取到的inSampleSize值再次解析图片
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFile(pathName, options);
return bitmap;
}
private int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight)
{
// 源图片的宽度
int width = options.outWidth;
int height = options.outHeight;
int inSampleSize = 1;
if (width > reqWidth && height > reqHeight)
{
// 计算出实际宽度和目标宽度的比率
int widthRatio = Math.round((float) width / (float) reqWidth);
int heightRatio = Math.round((float) width / (float) reqWidth);
inSampleSize = Math.max(widthRatio, heightRatio);
}
return inSampleSize;
}