ImageLoader

ImageLoader

代码来源于android艺术开发探索 12章,主要为了减少加载图片的资源损耗,利用了下面2个知识点
1 LruCache and DiskLruCache两个缓冲
其中LruCache相当于一级缓存 , DiskLruCache相当于二级缓存,从LruCache中读取图片的速度最快,接下来是 DiskLruCache ,最慢就是从web服务器获取 , 所以在第一次我们会把数据网上下载下来后缓存到Disk和Memory中,下次的读取速度就会很快,注意的是在主线上可以进行LruCache操作,但是不建议这样进行 , DiskLruCache操作只能在后台线程执行
2 bitmap sacle缩放减少内存消耗
下面分析代码

博主git源代码: https://git.oschina.net/qianlilo/ImageLoader_util.git

Bitmap缩放

    #为什么要缩放哪? android给每个应用程序的内存是固定的 ,64M ,内存是宝贵的资源,
    # 但是,一张图片长和宽都扩大到2倍,内存要扩大到4倍,但是由于人眼的限制或者显示器的限制,增加分辨率效果并不明显,所以我们要使用合适的分辨率

    /**
     * get bitmap from resourceId
     * @param resId  resource Id
     * @param reqWidth  require width
     * @param reqHeight  require height
     * */
    public Bitmap decodeSampleBitmapFromResource(Resources res,
                                                 int resId , int reqWidth , int reqHeight){

        final BitmapFactory.Options options = new BitmapFactory.Options();
        //only get pic size , in this do not consume real data
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res , resId , options);

        options.inSampleSize = calculateInSampleSize(options , reqWidth , reqHeight);

        //can get real data from pic
        options.inJustDecodeBounds = false;

        return BitmapFactory.decodeResource(res , resId , options);
    }

     /**
     * calculate shrink resolution
     * @return more than 1
     * @param options  pic info
     * @param reqHeight require height
     * @param reqWidth  require width
     * */
    private int calculateInSampleSize(BitmapFactory.Options options,
                                      int reqWidth, int reqHeight) {
        //judge argment
        if(reqHeight == 0 || reqWidth == 0){
            return  1;
        }
        final int height = options.outHeight;
        final int width = options.outWidth;
        Log.d(TAG, "origin pic { w = " +width + ", h = "+height+" } ");
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {
            final int halfHeight = height / 2;
            final int halfWidth = width / 2;
            while ( (halfHeight/inSampleSize) >= reqHeight
                    && (halfWidth/inSampleSize) >= reqWidth) {
                inSampleSize *= 2;
            }
        }
        return inSampleSize;
    }

线程池的使用

    #在这里使用线程池是因为每个图片都会去执行一个一次,开启多线程可以减少重复开启线程的资源开销
     private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
    private static final int MAXNUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final long KEEP_ALIVE = 10L;
    private static final ThreadFactory sThreadFacory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);
        @Override
        public Thread newThread(@NonNull Runnable r) {
            return new Thread(r , "ImageLoader#"+mCount.getAndIncrement());
        }
    };
    public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
            CORE_POOL_SIZE , MAXNUM_POOL_SIZE , KEEP_ALIVE , TimeUnit.SECONDS ,
            new LinkedBlockingQueue<Runnable>() , sThreadFacory);

LruChache的使用

    #由于lrucache是官方的,我们可以直接使用
    #我们选取一个应用程序最大可以内存的1/8位缓存
        int maxMemory = (int) (Runtime.getRuntime().maxMemory()/1024);
        int cacheSize = maxMemory/8;
        LruCache mMemoryCache = new LruCache<String, Bitmap>(cacheSize){
            @Override
            #计算每张图片的大小 , 根据代码是这个意思
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
            }
        };

    /**
     * add bitmap to memory cache 缓存到内存
     * */
    private void addBitmapToMemoryCache(String key,Bitmap bitmap){
        if (getBitmapFromMemCache(key) == null) {
            mMemoryCache.put(key , bitmap);
        }
    }

    /**
     *  get bitmap from memory cache 从内存得到缓存
     * */
    private Bitmap getBitmapFromMemCache(String key){
        return mMemoryCache.get(key);
    }

    #可以发现不管是存还是取都很简单 , 书的作者这里使用了URl通过MD5得到key
    #个人观点觉得在这里是有信息熵没什么必要,本来的URL就是明文,关于加密简单使用可以看我的另一个博客 
    # [这里写链接内容](http://blog.csdn.net/chenqianleo/article/details/76736575%20java%20%E4%BF%A1%E6%81%AF%E7%86%B5%20%E5%8A%A0%E5%AF%86)

    private String hashKeyFromUrl(String url){
        String cacheKey;
        try {
            final MessageDigest mDigest = MessageDigest.getInstance("MD5");
            mDigest.update(url.getBytes());
            cacheKey = bytesToHexString(mDigest.digest());
        } catch (NoSuchAlgorithmException e) {
            cacheKey = String.valueOf(url.hashCode());
            e.printStackTrace();
        }
        return cacheKey;
    }
     private String bytesToHexString(byte[] bytes){
        StringBuilder sb = new StringBuilder();
        int len = bytes.length;
        for (int i=0;i<len;i++){
            String hex = Integer.toHexString(0xFF & bytes[i]);
            if(hex.length() == 1){
                sb.append('0');
            }
            sb.append(hex);
        }
        return sb.toString();
    }

DiskLruCache

    #不是官方的,但是官方推荐这样使用,所以我们要直接拷贝一个DiskLruCache.java文件到项目,具体的可以上网或者下载源码来得到
     DiskLruCache mDiskLruCache = DiskLruCache.open(diskCacheDir , 1 , 1 , DISKC_CACHE_SIZE);

     #存 ,这里是先通过网络下载在缓存得到diskcache
     private Bitmap loadBitmapFromHttp(String uri, int reqWidth, int reqHeight) throws IOException {
       #在UI线程不可以使用网络耗时操作
        if(Looper.myLooper() == Looper.getMainLooper()){
            throw new RuntimeException("can not visit netWork from UI thread");
        }
        if(mDiskLruCache == null){
            return null;
        }

        String key = hashKeyFromUrl(uri);
        #开始存
        DiskLruCache.Editor editor = mDiskLruCache.edit(key);
        if (editor != null) {
            OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX);
            //这个函数是把url的内容存到outputstream中去
            if (downloadUrlToStream(uri, outputStream)) {
                editor.commit();
            } else {
                editor.abort();
            }
            mDiskLruCache.flush();
        }
        return loadBitmapFromDiskCache(uri , reqWidth , reqHeight);
    }

     #取
    private Bitmap loadBitmapFromDiskCache(String uri, int reqWidth, int reqHeight)
            throws IOException {
        if(Looper.myLooper() == Looper.getMainLooper()){
            Log.e(TAG, " load bitmap from UI thread, not recommended" );
        }
        if(mDiskLruCache == null){
            return null;
        }

        Bitmap bitmap = null;
        String key = hashKeyFromUrl(uri);
        DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
        if(snapshot != null){
            FileInputStream fileInputStream = (FileInputStream) snapshot.getInputStream(DISK_CACHE_INDEX);
            FileDescriptor fileDescriptor = fileInputStream.getFD();
            #自己实现的,使用了上面介绍的缩放
            bitmap = mImageResizer.decodeSampleBitmapFromFileDescriptor(fileDescriptor ,
                    reqWidth , reqHeight);
            # 内存缓存中没有 , 缓存到内存中去        
            if(bitmap != null){
                addBitmapToMemoryCache(key , bitmap);
            }
        }
        return bitmap;
    }

完整的代码实现了照片墙的功能,可以下载代码看完整demo

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值