利用缓存与多线程(线程池管理)加载,解决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
- 利用缓存与多线程(线程池管理)加载,解决RecycleView显示较多图片时出现的卡顿问题
- 解决RecycleView嵌套RecycleView滑动卡顿的问题
- ListView卡顿优化过程,并解决与viewpager图片加载冲突的问题
- android 加载本地所有视频时,显示其第一帧出现的卡顿问题
- Recycleview嵌套Recycleview时出现的一个问题-待解决
- ListView只加载当前屏幕内的图片(解决list滑动时加载卡顿)
- 在使用Recyclerview加载列表圆形图片时出现的滑动卡顿及图片混乱问题
- 解决UINavigationController在pushViewController时出现的"卡顿"问题
- 关于RecycleView嵌套Recycleview卡顿的问题
- Android相册解决加载大量图片卡顿问题
- 【BUG解决】Android ListView只加载当前屏幕内的图片(解决list滑动时加载卡顿)
- 网络图片异步加载(用到多线程(线程池),java回调机制,图片缓存,图片的动画)
- ViewPager与recycleView同时使用时出现的View加载空白问题
- Android ListView只加载当前屏幕内的图片(解决list滑动时加载卡顿)
- Android ListView只加载当前屏幕内的图片(解决list滑动时加载卡顿)
- Android ListView只加载当前屏幕内的图片(解决list滑动时加载卡顿)
- Android ListView只加载当前屏幕内的图片(解决list滑动时加载卡顿)
- Android ListView只加载当前屏幕内的图片(解决list滑动时加载卡顿)
- SQL case when
- 银商大华捷通平台对接代收款接口规范
- 读书笔记《Effective C++》条款17:以独立语句将newed对象置入智能指针
- 介绍一下Objective-c常用的函数,常数变量
- Modbus TCP指令格式说明
- 利用缓存与多线程(线程池管理)加载,解决RecycleView显示较多图片时出现的卡顿问题
- Head First html and css学习纪录
- JavaScript标准的发展历程
- web.js.字符串操作与正则表达式.随笔
- 七种内容策略提高网站价值
- C/C++: 三元运算符“ ? : ”的用法
- 简单扩展Spring boot 中的 Redis 模板方法
- DirectX 学习笔记
- jQuery css() 方法 修改元素的属性 比如字体颜色、大小