zoukankan      html  css  js  c++  java
  • 使用LruCache和DiskLruCache手写一个ImageLoader

    一、概述

      在分析OkHttp3的缓存机制之前先手写一个实现了三级缓存的ImageLoader来整体感受一下LruCache和DiskLruCache的用法。本例实现了三级缓存,利用LruCache实现内存缓存,利用DiskLruCache实现磁盘缓存。整体的流程是:当用户请求一张图时,首先检查内存中是否有缓存图片,如果有就直接返回,如果没有就检查磁盘中是否有,有就返回,没有就启用网络下载图片,然后把图片缓存在磁盘缓存和内存缓存中。下面看看具体的实现步骤。

    二、源程序介绍

      1.ImageLoader.java:是一个单例类,是加载图片的入口.

      2.CacheManager.java用来管理LruCacheManger和DiskLruCacheManager

      3.LruCacheManager.java是用来管理内存缓存的。

      4.DiskLruCacheManager是用来管理磁盘缓存的

      5.DownloadFileUtil.java是用来下载文件的,本例指的是图片

      6.MainThreadHandler.java是创建了一个运行在主线程中的Handler

      7.Util.java工具类

      8.MsgConfig.java配置类

      9.LoaderResult.java存储view、bitmap和uri的工具类

      10.ImageResizer.java用来缩放图片用的

      整体类图结构

     ImageLoader.java中就放了一个线程池(自定义线程池)外加displayView显示方法

    public class ImageLoader {
        private ImageLoader(Context context) {
            mainThreadHandler = new MainThreadHandler();
            cacheManager = new CacheManager(context, THREAD_POOL_EXECUTOR, mainThreadHandler);
        }
    
        private static final String TAG = "ImageLoader";
        public static final int MESSAGE_POST_RESULT = 1;
        //核心Cpu数量
        private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
        private static final int CORE_POOL_SIZE = CPU_COUNT + 1;//核心线程数量
        //线程池的最大容量
        private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
        private static final long KEEP_ALIZE = 10l;
    
    
        private static final ThreadFactory threadFactory = new ThreadFactory() {
            private final AtomicInteger mCount = new AtomicInteger(1);
    
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "ImageLoader#" + mCount.getAndIncrement());
            }
        };
        //创建一个线程池
        public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIZE, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(), threadFactory);
        private Context context;
        private CacheManager cacheManager;
        private MainThreadHandler mainThreadHandler;
        private static ImageLoader imageLoader = null;
        public static ImageLoader getDefault(Context context) {
            synchronized (ImageLoader.class){
                if(imageLoader==null){
                    imageLoader = new ImageLoader(context);
                }
            }
            return imageLoader;
        }
    
    
    
        /**
         * 异步加载一张图片并显示在view上
         *
         * @param url
         * @param view
         */
        public void displayView(String url, View view) {
    
            cacheManager.bindBitmap(url, view);
        }
    
        /**
         * 根据url获取一个bitmap
         *
         * @param url
         * @param reqWidth
         * @param reqHeight
         * @return
         */
        public Bitmap getBitmap(String url, int reqWidth, int reqHeight) {
            return cacheManager.loadBitmap(url, reqWidth, reqHeight);
        }
    
        /**
         * 异步加载指定图片的大小加载图片
         *
         * @param url
         * @param view
         * @param reqWidth
         * @param reqHeight
         */
        public void displayView(String url, View view, int reqWidth, int reqHeight) {
            cacheManager.bindBitmap(url, view, reqWidth, reqHeight);
        }
    
    
    }
    

     CacheManager.java用来管理磁盘缓存 和内存缓存,并实现三级缓存的判断

    package com.yw.library;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.drawable.BitmapDrawable;
    import android.os.Build;
    import android.os.Looper;
    import android.os.StatFs;
    import android.util.LruCache;
    import android.view.View;
    import android.widget.ImageView;
    
    import com.jakewharton.disklrucache.DiskLruCache;
    
    import java.io.File;
    import java.io.FileDescriptor;
    import java.io.FileInputStream;
    import java.io.OutputStream;
    import java.security.MessageDigest;
    import java.util.concurrent.Executor;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.ThreadPoolExecutor;
    
    /**
     * 缓存管理类
     * create by yangwei
     * on 2020-03-01 22:13
     */
    public class CacheManager {
        private ImageResizer imageResizer = new ImageResizer();
        private Executor THREAD_POOL_EXECUTOR;
        //磁盘缓存的最大容量
        public static final long DISK_CACHE_SIZE = 1024 * 1024 * 50;
        public static final int DISK_CACHE_INDEX = 0;
        private LruCacheManager lruCacheManager;
        private DiskLruCacheManager diskLruCacheManager;
        private MainThreadHandler mainThreadHandler;
        private Context context;
        public CacheManager(Context context,Executor THREAD_POOL_EXECUTOR,MainThreadHandler mainThreadHandler) {
            this.THREAD_POOL_EXECUTOR = THREAD_POOL_EXECUTOR;
            this.mainThreadHandler = mainThreadHandler;
            this.context = context;
            lruCacheManager = new LruCacheManager();
            diskLruCacheManager = new DiskLruCacheManager(context,imageResizer,lruCacheManager);
        }
    
    
        public void bindBitmap(final String uri, final View view) {
            bindBitmap(uri, view, 0, 0);
        }
    
        public void bindBitmap(final String uri, final View view, final int reqWidth, final int reqHeight) {
            view.setTag(MsgConfig.TAG_KEY_URI, uri);
            Bitmap bitmap = loadBitmapFromMemoryCache(uri);
            if (bitmap != null) {
                if (view instanceof ImageView) {
                    ((ImageView) view).setImageBitmap(bitmap);
                } else {
                    view.setBackground(new BitmapDrawable(bitmap));
                }
                return;
            }
            //利用线程池在后台加载图片,加载成功后进入主线程更新UI
            THREAD_POOL_EXECUTOR.execute(new Runnable() {
                @Override
                public void run() {
                    Bitmap bitmap = loadBitmap(uri, reqWidth, reqHeight);
                    LoaderResult loaderResult = new LoaderResult(view, uri, bitmap);
                    //向主线程中发送loaderresult
                    mainThreadHandler.obtainMessage(MsgConfig.MESSAGE_POST_RESULT, loaderResult).sendToTarget();
                }
            });
        }
    
        public Bitmap loadBitmap(String uri, int reqWidth, int reqHeight) {
            Bitmap bitmap = loadBitmapFromMemoryCache(uri);
            if (bitmap != null) {
                return bitmap;
            }
            bitmap = diskLruCacheManager.loadBitmapFromDiskCache(uri, reqHeight, reqHeight);
            if (bitmap != null) {
                return bitmap;
            }
            bitmap = loadBitmapFromHttp(uri, reqWidth, reqHeight);
            if (bitmap != null) {
                return bitmap;
            }
            if (bitmap == null && !diskLruCacheManager.mIsDiskLruCacheCreated) {
                bitmap = DownloadFileUtil.get().downloadBitmapFromUrl(uri);
            }
            return bitmap;
        }
    
        private Bitmap loadBitmapFromMemoryCache(String url) {
            final String key = Util.hashKeyFromUrl(url);
            Bitmap bitmap = lruCacheManager.getBitmapFromMemoryCache(key);
            return bitmap;
        }
    
        private Bitmap loadBitmapFromHttp(String url, int reqWidth, int reqHeight) {
            diskLruCacheManager.addBitmapToDiskCache(url);
            return diskLruCacheManager.loadBitmapFromDiskCache(url, reqWidth, reqHeight);
        }
    
        public static class Builder {
            public Builder(){}
    
        }
    
    }
    

      LruCacheManager.java用来管理LruCache

    package com.yw.library;
    
    import android.graphics.Bitmap;
    import android.util.LruCache;
    
    /**
     * create by yangwei
     * on 2020-03-01 22:30
     */
    public class LruCacheManager {
        private LruCache<String, Bitmap> mMemoryCache = null;
    
        public LruCacheManager() {
            int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
            int cacheSize = maxMemory / 8;
            mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
                @Override
                protected int sizeOf(String key, Bitmap value) {
                    //返回bitmap的大小,单位是kb
                    return value.getRowBytes() * value.getHeight() / 1024;
                }
            };
        }
    
        /**
         * 相内存缓存中添加一个Bitmap
         *
         * @param key
         * @param bitmap
         */
        public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
            if (getBitmapFromMemoryCache(key) == null) {
                mMemoryCache.put(key, bitmap);
            }
        }
    
        /**
         * 从内存缓存中取出一个Bitmap
         *
         * @param key
         * @return
         */
        public Bitmap getBitmapFromMemoryCache(String key) {
            return mMemoryCache.get(key);
        }
    }
    

      DissLruCacheManager用来管理DiskLruCache

    package com.yw.library;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.os.Build;
    import android.os.Looper;
    import android.os.StatFs;
    
    import com.jakewharton.disklrucache.DiskLruCache;
    
    import java.io.File;
    import java.io.FileDescriptor;
    import java.io.FileInputStream;
    import java.io.OutputStream;
    
    /**
     * 磁盘缓存管理
     * create by yangwei
     * on 2020-03-01 22:30
     */
    public class DiskLruCacheManager {
        private DiskLruCache mDiskLruCache = null;
        private Context context;
        public boolean mIsDiskLruCacheCreated = false;
        private ImageResizer imageResizer;
        private LruCacheManager lruCacheManager;
    
        /**
         * 初始化参数
         *
         * @param context
         * @param imageResizer
         * @param lruCacheManager
         */
        public DiskLruCacheManager(Context context, ImageResizer imageResizer, LruCacheManager lruCacheManager) {
            this.context = context;
            this.imageResizer = imageResizer;
            this.lruCacheManager = lruCacheManager;
            //创建磁盘缓存路径
            File diskCacheDir = FileUtil.createDiskCacheDir(context, "bitmap");
            if (!diskCacheDir.exists()) {
                diskCacheDir.mkdirs();
            }
            if (getUsableSpace(diskCacheDir) > CacheManager.DISK_CACHE_SIZE) {
                try {
                    mDiskLruCache = DiskLruCache.open(diskCacheDir, 1, 1, CacheManager.DISK_CACHE_SIZE);
                    mIsDiskLruCacheCreated = true;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
        /**
         * 添加Bitmap到磁盘
         *
         * @param url
         */
        public void addBitmapToDiskCache(String url) {
            if (Looper.myLooper() == Looper.getMainLooper()) {
                throw new RuntimeException("can not visit network form Ui Thread");
            }
            try {
                String key = Util.hashKeyFromUrl(url);
                DiskLruCache.Editor editor = mDiskLruCache.edit(key);
                if (editor != null) {
                    OutputStream outputStream = editor.newOutputStream(CacheManager.DISK_CACHE_INDEX);
                    if (DownloadFileUtil.get().downloadUrlToStream(url, outputStream)) {
                        editor.commit();
                    } else {
                        editor.abort();
                    }
                    mDiskLruCache.flush();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 从磁盘中加载图片
         *
         * @param url
         * @param reqWidth
         * @param reqHeight
         * @return
         */
        public Bitmap loadBitmapFromDiskCache(String url, int reqWidth, int reqHeight) {
            if (Looper.myLooper() == Looper.getMainLooper()) {
                throw new RuntimeException("can not load bitmap from UI Thread");
            }
            if (mDiskLruCache == null) {
                return null;
            }
            Bitmap bitmap = null;
            try {
                String key = Util.hashKeyFromUrl(url);
                DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
                if (snapshot != null) {
                    FileInputStream fileInputStream = (FileInputStream) snapshot.getInputStream(CacheManager.DISK_CACHE_INDEX);
                    FileDescriptor fileDescriptor = fileInputStream.getFD();
                    bitmap = imageResizer.decodeBitmapFromFileDescriptor(fileDescriptor, reqWidth, reqHeight);
                    try {
                        if (bitmap.getHeight() > 0) {
                            if (bitmap != null) {
                                lruCacheManager.addBitmapToMemoryCache(key, bitmap);
                            }
                        }
                    } catch (Exception e) {
                        return null;
                    }
    
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return bitmap;
        }
    
        public long getUsableSpace(File path) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
                return path.getUsableSpace();
            }
            final StatFs statFs = new StatFs(path.getPath());
            return statFs.getBlockSizeLong() * statFs.getAvailableBlocksLong();
        }
    
    }
    

    总结:整体逻辑的构建基本上都在CacheManager中,逻辑还是比较清楚的。

    ps:完整代码在github,有需要的可以去下载。下载路径如下:

    去github下载完整源码

     

  • 相关阅读:
    ASPNET下的路径辅助类
    分析函数计算起始,结束日期.
    Debugging SQL Server 2005 Stored Procedures in Visual Studio
    Storing Binary Files Directly in the Database Using ASP.NET 2.0
    Fw:Managing View State in ASP.NET 4 Using the New ViewStateMode Property
    Using ASP.NET 3.5's ListView and DataPager Controls: Displaying Data with the ListView
    Passing Information Between Content and Master Pages .
    转:Querying a Hierarchical ParentChild Structure in LINQ
    续上篇:比较彻底的清除"代理木马下载器"的方法
    面向过程和面向对象--从C到C#
  • 原文地址:https://www.cnblogs.com/tony-yang-flutter/p/12394761.html
Copyright © 2011-2022 走看看