您的位置:首页 > 其它

利用缓存与多线程(线程池管理)加载,解决RecycleView显示较多图片时出现的卡顿问题

2017-05-13 14:15 741 查看
本文以利用RecycleView加载所有本地图片为例,抛砖引玉。

提前需要稍微了解的概念:

外存读取、图片压缩、(IPC通信中的)信号量、线程池、缓存、单例模式(加载类,缓存类均使用了单例模式)、LRU算法以及RecycleView的基本使用

过程示意图:



注意:

加载获取Bitmap的过程中需要对ImageView进行测量,然后根据需要适当压缩Bitmap,为了达到更好的效果可能需要对图片进行进行剪切后压缩等操作等。

先从RecycleView说起:

这里对于RecycleView的基本使用就不再描述,要想实现RecycleView多线程加载本地图片的效果,需要:

1、获取在Adapter中获取所有本地图片的路径并保存在List中,用到了ContentProvider、游标Cursor;

private List<String> initPaths(){
List<String> paths = new ArrayList<>();
ContentResolver cr = context.getContentResolver();
if (Environment.getExternalStorageState().equals(Environment.MEDIA_UNKNOWN)){
Log.e(TAG, "no external storage");
return null;
}
Uri mImgUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
Cursor cursor = cr.query(mImgUri, null,
MediaStore.Images.Media.MIME_TYPE + " = ? or "
+ MediaStore.Images.Media.MIME_TYPE + " = ? or "
+ MediaStore.Images.Media.MIME_TYPE + " = ? ",
new String[]{"image/jpeg", "image/png", "image/jpg"},
MediaStore.Images.Media.DATE_MODIFIED );

while (cursor.moveToNext()){
String path = cursor.getString(cursor
.getColumnIndex(MediaStore.Images.Media.DATA));
paths.add(path);
Log.d(TAG, path);
}
return paths;
}


2、在适配器的onBinder方法中设置Bitmap、后台实现多线程的图片加载过程,加载类使用了单例模式;

Load.getInstance().loadImage(((MyHolder)holder).imageView, list.get(position));


3、加载类的实现如下, 利用信号量的PV来控制线程池中线程的最大运行数、同时用来控制变量初始化与使用的先后(避免空指针异常), ImageView的测量与图片的压缩;

public class Load {

private final static int THREAD_COUNT = 4;

private static Load mInstance;
private ExecutorService threadPool;

private Thread loopThread;
private LinkedList<Runnable> taskQuene = new LinkedList<>();
private Handler mHandler;
private Handler mUIHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
Holder holder = (Holder) msg.obj;
Bitmap bm = holder.bitmap;
ImageView imageView = holder.imageView;
String path = holder.path;
if(imageView.getTag().toString().equals(path)){
imageView.setImageBitmap(bm);
}
}
};
private Semaphore threadPoolTaskSemaphore;
private Semaphore mHandlerSemaphore;

private Load(int threadCount, final Type type){
loopThread = new Thread(){
@Override
public void run() {
Looper.prepare();
mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
try {
threadPoolTaskSemaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
threadPool.execute(getTask(type));
}
};
mHandlerSemaphore.release();
Looper.loop();
}
};
loopThread.start();
threadPool = Executors.newFixedThreadPool(threadCount);
threadPoolTaskSemaphore = new Semaphore(threadCount);
mHandlerSemaphore = new Semaphore(0);
}

public static Load getInstance(){
if (mInstance == null){
synchronized (Load.class){
if (mInstance == null){
mInstance = new Load(THREAD_COUNT, Type.FILO);
}
}
}
return mInstance;
}

private synchronized void addTask(Runnable r){
taskQuene.add(r);
if (mHandler == null){
try {
mHandlerSemaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
mHandler.sendEmptyMessage(0x010);
}

private synchronized Runnable getTask(Type type){
if (type == Type.FIFO){
return taskQuene.removeLast();
}else if (type == Type.FILO){
return taskQuene.removeFirst();
}else {
return null;
}
}

private Bitmap getBitmapFormCache(String path) {
return MyCache.getInstacne().getCache().get(path);
}

private void saveBitmapToCache(String path, Bitmap bitmap){
MyCache.getInstacne().getCache().put(path, bitmap);
}

private Bitmap readBitmapFromFileDescriptor(String filePath, int reqWidth, int reqHeight) {
try {
FileInputStream fis = new FileInputStream(filePath);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;

BitmapFactory.decodeFileDescriptor(fis.getFD(), null, options);
final int width = options.outWidth;
final int height = options.outHeight;
int inSampleSize = 1;

if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round(height / reqHeight);
} else {
inSampleSize = Math.round(width / reqWidth);
}
}

options.inJustDecodeBounds = false;
options.inSampleSize = inSampleSize;

return BitmapFactory.decodeFileDescriptor(fis.getFD(), null, options);
} catch (Exception ex) {
}
return null;
}

enum Type{
FIFO, FILO
}

p
a44d
ublic void loadImage(final ImageView imageView, final String path){
imageView.setImageBitmap(null); //防止闪屏
imageView.setTag(path);
final ImageSize imageSize = getImageViewSize(imageView);
Bitmap bitmap = getBitmapFormCache(path);
if (bitmap == null){
//Add runnable to taskQueue;
addTask(new Runnable() {
@Override
public void run() {
Bitmap bp = readBitmapFromFileDescriptor(path, imageSize.width, imageSize.height);
saveBitmapToCache(path, bp);
refreshUI(bp, imageView, path);
threadPoolTaskSemaphore.release();
}
});
}else {
refreshUI(bitmap, imageView, path);
}

}

private void refreshUI(Bitmap bitmap, ImageView imageView, String path){
Holder holder = new Holder();
holder.bitmap = bitmap;
holder.imageView = imageView;
holder.path = path;

Message message = Message.obtain();
message.obj = holder;
mUIHandler.sendMessage(message);
}
/**
* 根据ImageView获得适当的压缩的宽和高对图片进行压缩,防止内存溢出
* */
private ImageSize getImageViewSize(ImageView imageView) {
ImageSize imageSize = new ImageSize();

DisplayMetrics displayMetrics = imageView.getContext().getResources().getDisplayMetrics();

ViewGroup.LayoutParams lp = imageView.getLayoutParams();

//压缩宽度
int width = imageView.getWidth();
if (width <= 0){
width = lp.width; //获得imageView再layout中声明的宽度
}
if (width <= 0){
width = imageView.getMaxWidth();//检查最大值
}
if(width <= 0){
width = displayMetrics.widthPixels;
}
//压缩高度
int height = imageView.getHeight();
if (height <= 0){
height = lp.height; //获得imageView再layout中声明的宽度
}
if (height <= 0){
height = imageView.getMaxHeight();//检查最大值
}
if(height <= 0){
height = displayMetrics.heightPixels;
}

imageSize.height = height;
imageSize.width = width;
return imageSize;
}

private class ImageSize{
int width;
int height;
}
class Holder{
Bitmap bitmap;
String path;
ImageView imageView;
}


4、缓存类的实现:public class MyCache {
private LruCache<String, Bitmap> cache;
private static MyCache mInstance;
private final int PER_FOR = 4;

public static MyCache getInstacne(){
if (mInstance == null){
synchronized (MyCache.class){
if (mInstance == null){
mInstance = new MyCache();
}
}
}
return mInstance;
}

public MyCache() {

int max = (int) Runtime.getRuntime().maxMemory();
int size = max/PER_FOR;

cache = new LruCache<String, Bitmap>(size){
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount();
}
};
}

public LruCache<String, Bitmap> getCache(){
return cache;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐