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

来源:互联网 发布:百度下载软件 编辑:程序博客网 时间:2024/05/16 17:03

本文以利用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    }    public 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;    }}

完整项目见:https://github.com/jiarWang/AndroidView/tree/master/LocalImage

0 0
原创粉丝点击