Android--imageLoader

package com.ncuhome.usconnection.cache;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.StatFs;
import android.support.v4.util.LruCache;
import android.util.Log;
import android.widget.ImageView;

import com.ncuhome.usconnection.R;
import com.ncuhome.usconnection.util.DiskLruCache;
import com.ncuhome.usconnection.util.ImageResizer;
import com.ncuhome.usconnection.util.MyUtils;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class ImageLoader {

    private static final String TAG = "ImageLoader";

    public static final int MESSAGE_POST_RESULT = 1;

    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;//最大容量为CPU2倍 +1 
    private static final long KEEP_ALIVE = 10L;//线程闲置超时时长 10S

    //
    private static final int TAG_KEY_URI = R.id.imageloader_uri;
    private static final long DISK_CACHE_SIZE = 1024 * 1024 * 50; //50M的硬盘内存
    private static final int IO_BUFFER_SIZE = 8 * 1024; //
    private static final int DISK_CACHE_INDEX = 0;
    private boolean mIsDiskLruCacheCreated = false;

    //线程工厂
    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        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_ALIVE, TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(), sThreadFactory);

    private Handler mMainHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            LoaderResult result = (LoaderResult) msg.obj;
            ImageView imageView = result.imageView;
            imageView.setImageBitmap(result.bitmap);
            String uri = (String) imageView.getTag(TAG_KEY_URI);
            //这里判断它的url是否发生变化,如果改变就不需要给它设置图片
            if (uri.equals(result.uri)) {
                imageView.setImageBitmap(result.bitmap);
            } else {
                Log.e(TAG, "set image bitmap,but url has changed, ignored!");
            }
        }
    };

    /**
     * build a new instance of ImageLoader
     *
     * @param context
     * @return a new instance of ImageLoader
     */
    public static ImageLoader build(Context context) {
        return new ImageLoader(context);
    }

    private Context mContext;
    private ImageResizer mImageResizer = new ImageResizer();
    private LruCache<String, Bitmap> mMemoryCache;
    private DiskLruCache mDiskLruCache;

    public ImageLoader(Context context) {
        mContext = context.getApplicationContext();
        //当前应用程序可用内存/1024   单位为KB
        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        int cacheSize = maxMemory / 8;
        //内存缓存的创建  ,还有获取get   添加操作 删除
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            //sizeof是计算缓存对象的大小/1024  单位为KB,特殊情况下会重写entryRemoved方法 移除旧缓存时候会用此方法,在这里进行一些资源回收的工作
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
            }
        };

        File diskCacheDir = getDiskCacheDir(mContext, "bitmap");
        if (!diskCacheDir.exists()) {
            //可以在不存在的目录中创建文件夹
            diskCacheDir.mkdirs();
        }
        if (getUsableSpace(diskCacheDir) > DISK_CACHE_SIZE) {
            try {
                //第一个参数就是目录,也可以指定SD卡上的缓存目录,
                //第二个参数版本号,当版本号改变时会清空之前的所有缓存文件,但是,很多情况下,版本号发生改变缓存文件还是有效的.
                //大三个参数单个节点所对应的数据的个数
                //第四个参数缓存的总大小 这里是50M
                mDiskLruCache = DiskLruCache.open(diskCacheDir, 1, 1, DISK_CACHE_SIZE);
                mIsDiskLruCacheCreated = true;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void addBitmapToMemoryCache(String key, Bitmap bitmap) {
        if (getBitmapFromMemCache(key) == null) {
            mMemoryCache.put(key, bitmap);
        }
    }

    private Bitmap getBitmapFromMemCache(String key) {
        return mMemoryCache.get(key);
    }

    /**
     * 异步加载接口
     * load bitmap from memory cache or disk cache or network async, then bind imageView and bitmap.
     * NOTE THAT: should run in UI Thread
     *
     * @param uri       http url
     * @param imageView bitmap‘s bind object
     */
    public void bindBitmap(Context mContext, final String uri, final ImageView imageView) {
        bindBitmap(mContext, uri, imageView, 0, 0);
    }

    public void bindBitmap(final Context mContext, final String uri, final ImageView imageView, final int reqWidth, final int reqHeight) {
        imageView.setTag(TAG_KEY_URI, uri);
        Bitmap bitmap = loadBitmapFromMemCache(uri);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            return;
        }

        Runnable loadBitmapTask = new Runnable() {
            @Override
            public void run() {
                Bitmap bitmap = loadBitmap(mContext, uri, reqWidth, reqHeight);
                if (bitmap != null) {
                    LoaderResult result = new LoaderResult(imageView, uri, bitmap);
                    mMainHandler.obtainMessage(MESSAGE_POST_RESULT, result).sendToTarget();
                }
            }
        };
        THREAD_POOL_EXECUTOR.execute(loadBitmapTask);
    }

    /**
     * 同步加载接口,会在异步借口中调用
     * load bitmap from memory cache or disk cache or network.
     *
     * @param uri       http url
     * @param reqWidth  the width ImageView desired
     * @param reqHeight the height ImageView desired
     * @return bitmap, maybe null.
     */
    public Bitmap loadBitmap(Context mContext, String uri, int reqWidth, int reqHeight) {
        Bitmap bitmap = loadBitmapFromMemCache(uri);
        if (bitmap != null) {
            Log.d(TAG, "loadBitmapFromMemCache,url:" + uri);
            return bitmap;
        }

        try {
            bitmap = loadBitmapFromDiskCache(uri, reqWidth, reqHeight);
            if (bitmap != null) {
                Log.d(TAG, "loadBitmapFromDisk,url:" + uri);
                return bitmap;
            }
            bitmap = loadBitmapFromHttp(mContext, uri, reqWidth, reqHeight);
            Log.d(TAG, "loadBitmapFromHttp,url:" + uri);
        } catch (IOException e) {
            e.printStackTrace();
        }

        if (bitmap == null && !mIsDiskLruCacheCreated) {
            Log.w(TAG, "encounter error, DiskLruCache is not created.");
            //从网络中获取
            bitmap = downloadBitmapFromUrl(mContext, uri);
        }

        return bitmap;
    }

    private Bitmap loadBitmapFromMemCache(String url) {
        final String key = hashKeyFormUrl(url);
        Bitmap bitmap = getBitmapFromMemCache(key);
        return bitmap;
    }

    private Bitmap loadBitmapFromHttp(Context mContext, String url, int reqWidth, int reqHeight) throws IOException {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            throw new RuntimeException("can not visit network from UI Thread.");
        }
        if (mDiskLruCache == null) {
            return null;
        }
        //在这里添加缓存,首先获取URL(图片的网络地址)对应的Key,
        String key = hashKeyFormUrl(url);
        //如果正在编辑就会返回null
        DiskLruCache.Editor editor = mDiskLruCache.edit(key);
        if (editor != null) {
            //在open中设置了一个节点只能有一个数据,所以这里设为0
            //图片可以通过这个文件输出流写入到文件系统上
            OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX);
            if (downloadUrlToStream(mContext, url, outputStream)) {
                editor.commit();
            } else {
                editor.abort();//退出整个编辑过程
            }
            mDiskLruCache.flush();
        }
        return loadBitmapFromDiskCache(url, reqWidth, reqHeight);
    }

    //从磁盘缓存中
    private Bitmap loadBitmapFromDiskCache(String url, int reqWidth, int reqHeight) throws IOException {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            Log.w(TAG, "load bitmap from UI Thread, it‘s not recommended!");
        }
        if (mDiskLruCache == null) {
            return null;
        }

        //DiskLruCache缓存查找 ,snapshot 得到文件输入流,有了输入流自然就有bitmap图片了
        Bitmap bitmap = null;
        String key = hashKeyFormUrl(url);
        DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
        if (snapShot != null) {
            FileInputStream fileInputStream = (FileInputStream) snapShot.getInputStream(DISK_CACHE_INDEX);
            //得到文件描述符
            FileDescriptor fileDescriptor = fileInputStream.getFD();

            bitmap = mImageResizer.decodeSampledBitmapFromFileDescriptor(fileDescriptor, reqWidth, reqHeight);
            if (bitmap != null) {
                addBitmapToMemoryCache(key, bitmap);
            }
        }
        return bitmap;
    }

    //通过文件输出流将图片写入到文件系统上
    public boolean downloadUrlToStream(Context mContext, String urlString, OutputStream outputStream) {
        HttpURLConnection urlConnection = null;
        BufferedOutputStream out = null;
        BufferedInputStream in = null;

        try {
            final URL url = new URL(urlString);
            SharedPreferences sp = mContext.getSharedPreferences("remember", Context.MODE_PRIVATE);
            String authorization = sp.getString("Authorization", null);
            Log.e("TAG", "标签1");
            urlConnection = (HttpURLConnection) url.openConnection();
            urlConnection.setRequestProperty("Authorization", authorization);
            in = new BufferedInputStream(urlConnection.getInputStream(), IO_BUFFER_SIZE);
            out = new BufferedOutputStream(outputStream, IO_BUFFER_SIZE);

            int b;
            while ((b = in.read()) != -1) {
                out.write(b);
            }
            return true;
        } catch (IOException e) {
            Log.e(TAG, "downloadBitmap failed." + e);
        } finally {
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
            MyUtils.close(out);
            MyUtils.close(in);
        }
        return false;
    }

    private Bitmap downloadBitmapFromUrl(Context mContext, String urlString) {
        Bitmap bitmap = null;
        HttpURLConnection urlConnection = null;
        BufferedInputStream in = null;

        SharedPreferences sp = mContext.getSharedPreferences("remember", Context.MODE_PRIVATE);
        String authorization = sp.getString("Authorization", null);
        try {
            Log.e("TAG", "标签2");
            final URL url = new URL(urlString);
            urlConnection = (HttpURLConnection) url.openConnection();
            urlConnection.setRequestProperty("Authorization", authorization);
            in = new BufferedInputStream(urlConnection.getInputStream(), IO_BUFFER_SIZE);
            //输入流解析成图片
            bitmap = BitmapFactory.decodeStream(in);
        } catch (final IOException e) {
            Log.e(TAG, "Error in downloadBitmap: " + e);
        } finally {
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
            MyUtils.close(in);
        }
        return bitmap;
    }

    //图片的URL转换为KEY
    private String hashKeyFormUrl(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());
        }
        return cacheKey;
    }

    //字节数组 转换为字符串
    private String bytesToHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(0xFF & bytes[i]);
            if (hex.length() == 1) {
                sb.append(‘0‘);
            }
            sb.append(hex);
        }
        return sb.toString();
    }

    //文件存储目录
    public File getDiskCacheDir(Context context, String uniqueName) {
        boolean externalStorageAvailable = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
        final String cachePath;
        if (externalStorageAvailable) {
            cachePath = context.getExternalCacheDir().getPath();
        } else {
            cachePath = context.getCacheDir().getPath();
        }

        return new File(cachePath + File.separator + uniqueName);
    }

    @TargetApi(VERSION_CODES.GINGERBREAD)
    private long getUsableSpace(File path) {
        if (Build.VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {
            return path.getUsableSpace();
        }
        final StatFs stats = new StatFs(path.getPath());
        return (long) stats.getBlockSize() * (long) stats.getAvailableBlocks();
    }

    private static class LoaderResult {
        public ImageView imageView;
        public String uri;
        public Bitmap bitmap;

        public LoaderResult(ImageView imageView, String uri, Bitmap bitmap) {
            this.imageView = imageView;
            this.uri = uri;
            this.bitmap = bitmap;
        }
    }
}
时间: 2024-08-02 20:00:23

Android--imageLoader的相关文章

Android -- ImageLoader本地缓存

传送门 <Android -- ImageLoader简析>  http://www.cnblogs.com/yydcdut/p/4008097.html 本地缓存 在缓存文件时对文件名称的修改提供了两种方式,每一种方式对应了一个Java类 1)  HashCodeFileNameGenerator,该类负责获取文件名称的hashcode然后转换成字符串. 2)  Md5FileNameGenerator,该类把源文件的名称同过md5加密后保存. 两个类都继承了FileNameGenerato

教你写Android ImageLoader框架之初始配置与请求调度

## 前言 在教你写Android ImageLoader框架之基本架构中我们对SimpleImageLoader框架进行了基本的介绍,今天我们就从源码的角度来剖析ImageLoader的设计与实现.   在我们使用ImageLoader前都会通过一个配置类来设置一些基本的东西,比如加载中的图片.加载失败的图片.缓存策略等等,SimpleImageLoader的设计也是如此.配置类这个比较简单,我们直接看源码吧. ImageLoaderConfig配置 /** * ImageLoader配置类,

教你写Android ImageLoader框架之图片缓存 (完结篇)

在教你写Android ImageLoader框架系列博文中,我们从基本架构到具体实现已经更新了大部分的内容.今天,我们来讲最后一个关键点,即图片的缓存.为了用户体验,通常情况下我们都会将已经下载的图片缓存起来,一般来说内存和本地都会有图片缓存.那既然是框架,必然需要有很好的定制性,这让我们又自然而然的想到了抽象.下面我们就一起来看看缓存的实现吧. 缓存接口 在教你写Android ImageLoader框架之图片加载与加载策略我们聊到了Loader,然后阐述了AbsLoader的基本逻辑,其中

教你写Android ImageLoader框架之图片加载与加载策略

在教你写Android ImageLoader框架之初始配置与请求调度中,我们已经讲述了ImageLoader的请求配置与调度相关的设计与实现.今天我们就来深入了解图片的具体加载过程以及加载的策略(包括按顺序加载和逆序加载) ,在这其中我会分享我的一些设计决策,也欢迎大家给我提建议. 图片的加载 Loader与LoaderManager的实现 在上一篇文章教你写Android ImageLoader框架之初始配置与请求调度中,我们聊到了Loader与LoaderManager. ImageLoa

教你写Android ImageLoader框架之基本架构 .

前言 在Android开发中,ImageLoader应该算得上是最重要的开源库之一,由于项目原因(不能使用开源库),前段时间自己也是需要实现一个简单的ImageLoader,因此诞生了这个库,我们暂且叫它为SimpleImageLoader.就目前而言,你上网查ImageLoader资料的时候,基本上能够找到很简单的实现,基本上一个类就把所有的工作给做了,这就显得很不专业了嘛,很多时候我们不只是需要实现功能,而是希望能够在实现功能的同时在设计层面有所提升. SimpleImageLoader分享

android ImageLoader加载本地图片的工具类

import android.widget.ImageView; import com.nostra13.universalimageloader.core.ImageLoader; /** * 异步加载本地图片工具类 * * @author tony * */ public class LoadLocalImageUtil { private LoadLocalImageUtil() { } private static LoadLocalImageUtil instance = null;

android ImageLoader 混淆加载drawable出现黑色图片的解决方案

在网上找了很久,没有找到.后来看了源码才知道... 多线程异步加载和显示图片(图片来源于网络.sd卡.assets文件夹,drawable文件夹(不能加载9patch),新增加载视频缩略图) String imageUri = "http://site.com/image.png"; // from Web String imageUri = "file:///mnt/sdcard/image.png"; // from SD card String imageUr

Android -- ImageLoader简析

图片的内存缓存实现 Image-Loader库有一个较完整的内存缓存实现,使用者可以根据需要选择已经实现的策略,也可以定制自己项目中需要的策略. 内存缓存实现代码在memory和memory.impl这两个包中,前者就是规范视图,后者是实现视图.memory包中有MemoryCacheAware接口和BaseMemoryCache和LimitedMemoryCache两个抽象类,加上memory.impl包中的WeakMemoryCache类. MemoryCacheAware MemoryCa

Android ImageLoader 本地缓存

本地缓存 在缓存文件时对文件名称的修改提供了两种方式,每一种方式对应了一个Java类 1)  HashCodeFileNameGenerator,该类负责获取文件名称的hashcode然后转换成字符串. 2)  Md5FileNameGenerator,该类把源文件的名称同过md5加密后保存. 两个类都继承了FileNameGenerator接口 在DefaultConfigurationFactory类中提供了一个工厂方法createFileNameGenerator,该方法返回了一个默认的F

Android开源库与设计模式开源组SAOS建立

Android开源库与设计模式开源组建立 简介 在2014年年底突然参与了CSDN的博客之星评选,看着自己的博客水平实在太低,于是就想一定得写一些跟别人不太一样的博客出来.经过自己的一番思考,觉得在Android开源库的深入实现上并没有什么太多的资料,或者只是大概讲述了一些基本原理.这样我觉得是不够的,很多事情你没有自己去经历你不会有很深的认识,或者你根本不知道原来它会出现这样的问题.于是我就想我没通过学习轮子制造过程来更加深入的学习,这样不仅能够了解那些知名的开源库,也能够从实战的角度学习开源