RecelerView + LruCache + ThreadPool 读取本地图片

原创 2016年06月01日 10:02:43

1.说点题外话

  • 最近正在找工作实习,发现工作真的不好找啊,感觉现在的Android不好找工作了,现在的公司都不愿意找没经验的,而我也只能一份一份的投简历。感叹完了,步入正题,这个Demo就是我面试的一家公司的笔试题。
  • 笔试题目

2.代码

  • 有可能v7包中没有RecelerView,可以dependencies中加入一句compile ‘com.android.support:recyclerview-v7:22.2.1’

1.代码结构

  • 这里写图片描述

2.核心代码

1.SearchManager

  • 在对sd卡根目录下的所有文件进行一次遍历,如果是文件,就判断是否符合要求,如果是文件夹就判断文件夹是否为空,如果不为空的话,就开一个子线程搜索文件夹下所有的文件(这应该不是最优算法,水平有限,就只能这样写了),判断是否符合要求。
  • 在搜索文件的时候为了节约时间,只获取了符合要求的文件名字和路径。
    /**
     * 搜索文件
     */
    public void startSearch() {
        //获取SD卡下面所有的文件
        File[] listFiles = mRootFile.listFiles();
        ArrayList<File> mFileList = new ArrayList<>();
        for (int i = 0; i < listFiles.length; i++) {
            if (listFiles[i].isDirectory()) {       //如果是文件夹
                File[] files = listFiles[i].listFiles();
                if (files.length > 0) {             //文件夹不为空
                    mTaskNum++;
                    mFileList.add(listFiles[i]);
                }
            } else {
                checkFile(listFiles[i]);             //检查文件是否符合要求
            }
        }

        //开启子线程搜索文件
        for (File file : mFileList) {
            SearchFileTask task = new SearchFileTask(file);
            ThreadManager.getThreadPool().execute(task);
        }
    }

    /**
     * 搜索文件任务
     */
    class SearchFileTask implements Runnable {

        private File mFile;

        public SearchFileTask(File file) {
            mFile = file;
        }

        @Override
        public void run() {
            search(mFile);
            onTaskFinish();
        }

    }

    /**
     * 搜索文件
     */
    public void search(File file) {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (int i = 0; i < files.length; i++) {
                if (files[i].isDirectory()) {
                    search(files[i]);         //如果是文件夹,就递归搜索该文件夹中的文件
                } else {
                    checkFile(files[i]);
                }
            }
        } else {
            checkFile(file);
        }
    }

2.ThreadManager

  • 在搜索文件和加载图片的时候采用了线程池来管理子线程。
    /**
     * 线程池
     */
    public static class ThreadPool {

        private int corePoolSize;                 //核心线程数
        private int maximumPoolSize;              //最大线程数
        private long keepAliveTime;               //休息时间
        private ThreadPoolExecutor mExecutor;

        private ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime) {
            this.corePoolSize = corePoolSize;
            this.maximumPoolSize = maximumPoolSize;
            this.keepAliveTime = keepAliveTime;
        }

        /**
         * 运行线程
         */
        public void execute(Runnable r) {
            if (mExecutor == null) {
                mExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
                        keepAliveTime, TimeUnit.SECONDS,
                        new LinkedBlockingDeque<Runnable>(),
                        Executors.defaultThreadFactory(),
                        new ThreadPoolExecutor.AbortPolicy());
            }

            //执行一个Runable对象
            mExecutor.execute(r);
        }

        /**
         * 取消线程
         */
        public void cancel(Runnable r) {
            if (mExecutor != null) {
                mExecutor.getQueue().remove(r);
            }
        }
    }

3.CacheUtils

  • 为了提高UI流畅度,所以在加载图片的时候采用了内存缓存机制。
public class CacheUtils {

    private LruCache<String, Bitmap> mMemoryCache;     //图片缓存

    public CacheUtils() {

        //初始化缓存
        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        int mCacheSize = maxMemory / 8;
        mMemoryCache = new LruCache<String, Bitmap>(mCacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getRowBytes() * value.getHeight() / 1024;
            }
        };
    }

    /**
     * 添加Bitmap到内存缓存
     */
    public synchronized void addBitmapToCacheMemory(String key, Bitmap bitmap) {
        if (getBitmapFromMemoryCache(key) == null && bitmap != null) {
            mMemoryCache.put(key, bitmap);
        }
    }

    /**
     * 从内存缓存中获取一个Bitmap
     */
    public Bitmap getBitmapFromMemoryCache(String key) {
        return mMemoryCache.get(key);
    }

}

4.LoadManager

  • 在读取图片的时候,为了节约内存,需要对图片做出一些处理。
  • 首先设置mOptions.inJustDecodeBounds = true;在不加载图片的情况下获取图片的大小,根据在手机屏幕上实际显示的图片宽度,计算出与图片实际宽度的inSampleSize值,以及等比例的图片高度,并对图片的质量做一定的压缩,再将mOptions.inJustDecodeBounds = false;才真正将图片加载进来,并且将mOptions保存到图片信息里,下一次读取不需要再次计算。
public class JpgImageInfo {

    //文件名字
    private String name;
    //文件路径
    private String path;
    //文件宽度
    private int width;
    //文件高度
    private int height;
    //图片的加载参数
    private BitmapFactory.Options options;
    /**
     * 从本地获取图片
     */
    public Bitmap getBitmap(JpgImageInfo imageInfo) {

        //如果不存在存在Options,就设置Options,再加载数据
        if (imageInfo.getOptions() == null) {
            //获取图片信息
            BitmapFactory.Options mOptions = new BitmapFactory.Options();

            //只获取图片信息,不加载图片
            mOptions.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(imageInfo.getPath(), mOptions);

            //图片处理后的宽高
            float scale = (float) mOptions.outWidth / mOptions.outHeight;
            int width = mWindowWidth / showLineNum - UIUtils.dip2px(showLineNum * 3);
            int height = (int) (width / scale);
            imageInfo.setWidth(width);
            imageInfo.setHeight(height);

            //从本地读取图片
            mOptions.inJustDecodeBounds = false;
            int inSampleSize = mOptions.outWidth / width;
            if (inSampleSize < 1) {
                inSampleSize = 1;
            }
            mOptions.inSampleSize = inSampleSize;
            mOptions.inPreferredConfig = Bitmap.Config.RGB_565;
            mOptions.inDither = true;
            imageInfo.setOptions(mOptions);
        }

        return BitmapFactory.decodeFile(imageInfo.getPath(),
                imageInfo.getOptions());
    }

5.JpgAdapter

  • 继承至RecyclerView.Adapter
  • 在item布局文件中imageview设置了100dp的宽高度,但是这个100dp,是没有实际意义的,它只是用占空间的,在RecyclerView初始化的时候用来限制RecyclerView加载的ViewHolder数量,在imageview设置图片前,根据图片信息中的宽高设置imageview的大小。
 //加载图片
        mLoadManager.loadImage(jpgImageInfo, new OnImageLoadListener() {

            @Override
            public void onImageLoad(JpgImageInfo imageInfo, Bitmap bitmap) {
                if (bitmap != null) {
                    setImageWidthAndHeight(holder.ivPic, imageInfo);
                    holder.ivPic.setImageBitmap(bitmap);
                }
            }
        });
    /**
     * 设置图片的高度和宽度
     */
    public void setImageWidthAndHeight(ImageView imageView, JpgImageInfo imageInfo) {

        ViewGroup.LayoutParams params = imageView.getLayoutParams();
        params.width = imageInfo.getWidth();
        params.height = imageInfo.getHeight();
        imageView.setLayoutParams(params);
    }

3.效果

  • 为了压缩到2M,画质有点渣
    这里写图片描述

4.源码

5.参考文献

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Android优化:Android 用LruCache读取大图片并缓存

前言:内存优化很重要。 图片预取缓存策略是内存缓存(硬引用LruCache、软引用SoftReference)、外部文件缓存(context.getCachedDir()),缓存中取不到的情况下...

android 用LruCache读取大图片并缓存

图片预取缓存策略是内存缓存(硬引用LruCache、软引用SoftReference)、外部文件缓存(context.getCachedDir()),缓存中取不到的情况下再向服务端请求下载图片。同时缓...

转《[图像处理/特效] android 用LruCache读取大图片并缓存》

图片预取缓存策略是内存缓存(硬引用LruCache、软引用SoftReference)、外部文件缓存(context.getCachedDir()),缓存中取不到的情况下再向服务端请求下载图片。同时缓...

Android Lrucache加载图片(AsyncTask )

  • 2015年10月15日 17:57
  • 1.25MB
  • 下载

RecyclerView条目加载图片实现LruCache三级缓存策略,有效避免 OOM , 提升系统流畅性

在项目中, 经常会用到列表展示 , RecyclerView 也好 , ListView 也罢 ,   当展示大量图片 , 此时若不做好缓存功能 , 耗费用户流量不说 , 经常会有OOM内存溢出的可能...
  • sxt_zls
  • sxt_zls
  • 2017年06月16日 15:31
  • 351

LruCache图片批量加载

  • 2017年06月14日 16:04
  • 43.89MB
  • 下载

图片缓存LruCache和DiskLruCache的使用

前言作为一个Android技术小白,感觉自己各方面知识与技能都存在欠缺、不足,知识体系不够系统。是时候整理一下学习知识和技术点,慢慢梳理,架构体系,如此才能有利于自身技术的更好发展。Android属于...

LruCache缓存网络图片

  • 2016年08月27日 16:57
  • 444KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:RecelerView + LruCache + ThreadPool 读取本地图片
举报原因:
原因补充:

(最多只允许输入30个字)