ImageLoader的实现
一般来说一个好的图片库应该具备如下功能
图片的同步加载;
图片的异步加载;
图片压缩;
内存缓存;
磁盘缓存;
网络拉取;
1.图片压缩功能的实现
public class ImageReSizer {
public Bitmap decodeSampleBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight){
//首先用inJustDecodeBounds = true 解码去确认dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res,resId,options);
//计算imSampleSize
options.inSampleSize = calculateSampleSize(options, reqWidth, reqHeight );
//用inSampleSIze解码bitmap
options.inJustDecodeBounds =false;
return BitmapFactory.decodeResource(res,resId,options);
}
public Bitmap decodeSampledBitmapFromFileDescriptor(FileDescriptor fd, int reqWidth, int reqHeight){
//首先用inJustDecodeBounds = true 解码去确认dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFileDescriptor(fd,null,options);
//计算inSampleSize
options.inSampleSize = calculateSampleSize(options, reqWidth, reqHeight);
//用inSampleSize集合去解码Bitmap
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFileDescriptor(fd,null,options);
}
private int calculateSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
if (reqWidth==0 || reqHeight==0) {
return 1;
}
//未处理的高度和宽度
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width >reqWidth) {
final int halfHeight = height/2;
final int halfWidth = width/2;
//计算最大的inSampleSize的2次方
//两个都保存
//宽高都比要求的宽高大
while ((halfHeight / inSampleSize) >= reqHeight &&
(halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *=2;
}
}
return inSampleSize;
}
}
**2.内存缓存和磁盘缓存的实现
这里选择LruCache和DiskLruCache来分别完成内存缓存和磁盘缓存的工作,在imageLoader初始化时 会创建LruCache和DiskLruCache。**
2.1 LruCache
LruCache是一个AndroidSdk提供的一个缓存泛型类, 它内部采用一个LinkedHashMap以强引用的方式储存外界的缓存对象,其提供了get和push方法来完成缓存的获取和添加操作,当缓存满时,LruCache会移除掉较早使用的缓存对象,然后再添加新的缓存对象。
强引用: 直接的对象引用
软引用: 当一个对象只有软引用存在时, 系统内存不足时此对象会被gc回收。
弱引用: 当一个对象只有弱引用存在时,此对象会随时被gc回收
一般初始化LruCache时, 指需要提供缓存的总容量大小并重写sizeOf方法即可。sizeOf方式是计算缓存对象的大小,这里大小的单位需要和总容量单位一样。
2.2 DiskLruCache
DiskLruCache 用户实现储存设备缓存,即磁盘缓存,它通过将缓存对象写入文件系统来实现缓存效果。 它的源码可以从如下网址得到:
官方地址
2.2.1 DiskLruCache的创建
DiskLruCache并不能通过构造方法来创建, 它提供了open方法来用于创建自身:
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
其中由四个参数分别是:
- 表示磁盘缓存在文件系统中的储存路径。 这里使用比较灵活, 当应用写在后就希望删除缓存文件,那么就选择sd卡上的缓存目录,如果希望保留缓存,就放在sd卡上的其他特定目录。
- 第二个参数表示的是应用的版本号,一般设置为1就好。
- 第三个参数表示单个节点所对应的数据个数。一般设置为1.
- 第四个参数表示缓存的总大小,比如50mb,当缓存大小超出这个设定后,缓存就会自动清楚一些缓存,保证大小不大于这个设定值。
2.3 在imageLoader中的实现
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.Environment;
import android.os.StatFs;
import android.util.LruCache;
import com.sihan.myview.libcore.io.DiskLruCache;
import java.io.File;
import java.io.IOException;
public class ImageLoader {
//磁盘缓存大小 50MB
private static final long DISK_CACHE_SIZE = 1024 * 1024 * 50;
//磁盘缓存是否创建
private boolean mIsDiskLruCacheCreated = false;
private LruCache<String, Bitmap> mMemoryCache;
private DiskLruCache mDiskLruCache;
private Context mContext;
/**
* 构造方法
* @param context
*/
private ImageLoader(Context context){
mContext = context.getApplicationContext();
//计算最大内存
int maxMemory = (int) (Runtime.getRuntime().maxMemory()/1024);
//设置内存缓存大小为内存的1/8
int cacheSize = maxMemory/8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize){
@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{
mDiskLruCache = DiskLruCache.open(diskCacheDir,1,1,DISK_CACHE_SIZE);
mIsDiskLruCacheCreated = true;
}catch (IOException e){
e.printStackTrace();
}
}
}
/**
* 获取磁盘缓存地址
* @param context
* @param uniqueName
* @return
*/
private File getDiskCacheDir(Context context, String uniqueName) {
//获取sdk状态 是否已经挂载并且拥有可读可写权限
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);
}
/**
* 获取剩余空间
* @param path
* @return
*/
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
private 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();
}
}
在创建磁盘缓存是做一判断, 由可能剩余空间小于磁盘缓存所需的,意思就是手机空间不足,无法创建磁盘缓存,这个时候磁盘缓存就会失效。
2.4 图片的获取
2.4.1 LruCache的添加和读取
private void addBitmapToMemoryCache(String key, Bitmap bitmap){
if (getBitmapFromMemCache(key) ==null){
mMemoryCache.put(key,bitmap);
}
}
private Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
很简单如果要获取就直接去LruCache中拿, 在添加的时候先判断缓存中是否存在,不存在的话就添加。
2.4.2 硬盘缓存的添加和读取
DiskLruCache的缓存添加的操作是通过Editor来完成的,Editor表示一个缓存对象的编辑对象。 这里仍然以图片缓存举例, 首先需要获取图片url所对应的key,然后根据key来可以通过edit()来获取Editor对象, 如果这个缓存正在编辑,那么edit()就会返回null, 即DiskLruCache不允许同时编辑一个缓存对象。 之所以要把url转换成key, 是因为图片的url中很可能由特殊自负,浙江影响url在安卓中的使用,一般去采用MD5作为key表示。
private String hashKeyFormUrl(String url) {
String cacheKey = null;
try{
final MessageDigest mDigest = MessageDigest.getInstance("MD5");
mDigest.update(url.getBytes());
cacheKey = bytesToHexString(mDigest.digest());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return cacheKey;
}
private String bytesToHexString(byte[] digest) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < digest.length; i++) {
String hex = Integer.toHexString(0xFF & digest[i]);
if (hex.length()==1) {
sb.append('0');
}
sb.append(hex);
}
return sb.toString();
}
将图片的url转化key以后,就可以获取Editor对象了。对于这个key来说,如果当前不存在其他Editor对象,那么edit()就会返回一个新的Editor对象,通过它就可以得到一个文件输出流。 需要注意的是,由于前面在DiskLruCache的open方法中设置了一个节点只能储存一个数据,因此下面的Disk_Cache_Index敞亮直接设为0即可:
有了文件输出流,当从网络中下载图片时,图片就可以通过这个文件输出流写入到文件系统中。代码如下所示:
private boolean downloadUrlToStream(String urlString, OutputStream outputStream) {
HttpURLConnection urlConnection = null;
BufferedOutputStream out = null;
BufferedInputStream in = null;
try{
final URL url = new URL(urlString);
urlConnection = (HttpURLConnection) url.openConnection();
in = new BufferedInputStream(urlConnection.getInputStream());
out = new BufferedOutputStream(urlConnection.getOutputStream());
int b;
while((b=in.read()) != -1){
out.write(b);
}
return true;
} catch (IOException e) {
e.printStackTrace();
}
finally {
if (urlConnection !=null){
urlConnection.disconnect();
MyUtils.close(in);
MyUtils.close(out);
}
}
return false;
}
文件读写
private Bitmap loadBitmapFromHttp(String url, int reqWidth, int reqHeight) throws IOException {
if (Looper.myLooper()==Looper.getMainLooper()){
Log.w(TAG, "can not visit network from UI thread" );
}
if (mDiskLruCache == null){
return null;
}
String key = hashKeyFormUrl(url);
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
if (editor != null){
OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX);
if (downloadUrlToStream(url,outputStream)){
editor.commit();
}else {
editor.abort();
}
mDiskLruCache.flush();
}
return loadBitmapFromDiskCache(url,reqWidth,reqHeight);
经过下载的文件并没有直接写在磁盘里,通过commit 去保存操作或者abort去撤销
硬盘缓存查找
private Bitmap loadBitmapFromDiskCache(String url, int reqWidth, int reqHeight) throws IOException {
//如果是用主线程去加载就提示
if (Looper.myLooper()==Looper.getMainLooper()){
Log.w(TAG, "loadBitmapFromDiskCache: load bitmap from UI Thread, it's not recommended!" );
}
//如果缓存不存在返回null
if (mDiskLruCache ==null){
return null;
}
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;
}
硬盘缓存查找需要需要将url转换为key,然后通过DiskLruCache的get方法得到一个Snapshot对象,接着在通过Snaphot对象即可得到缓存的文件输入流就可以得到bitmap对象了。这里通过文件流来饿到对应文件描述符,然后再通过BitmapFactory,decodeFileDescriptor方法来加载一张缩放后的图片。
3. 同步加载和异步加载接口的设计
3.1 同步加载
private Bitmap loadBitmap(String uri, int reqWidth, int reqHeight){
Bitmap bitmap = getBitmapFromMemCache(uri);
if (bitmap != null){
return bitmap;
}
try{
bitmap = loadBitmapFromDiskCache(uri, reqWidth, reqHeight);
if (bitmap != null){
return bitmap;
}
bitmap = loadBitmapFromHttp(uri, reqWidth, reqHeight);
} catch (IOException e) {
e.printStackTrace();
}
if (bitmap == null && !mIsDiskLruCacheCreated){
bitmap = downloadBitmapFromUrl(uri);
}
return bitmap;
}
其工作原理遵循如下:
- 尝试其内存缓存中获取图片
- 其磁盘缓存中获取图片
- 最后才从网络中拉去图片
另外这个方法不能在主线程中调用 否则就抛出异常。
3.2异步加载接口
private void bindBitmap(final String uri, final ImageView imageView, final int reqWidth, final int reqHeight){
imageView.setTag(TAG_KEY_URI, uri);
Bitmap bitmap = getBitmapFromMemCache(uri);
if (bitmap !=null){
imageView.setImageBitmap(bitmap);
return;
}
Runnable loadBitmapTask = new Runnable() {
@Override
public void run() {
Bitmap bitmap = loadBitmap(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);
}
从bindBitmao的实现来看,bindBitmap方法会尝试从那个内存缓存中读取图片,如果读取成功就直接返回结果,否则会在线程池中其调用loadBitmap方法,当图片呢加载成功后再将图片,图片的地址及需要绑定的imageView封装成一个LoaderResult对象,在通过hanlder向主线程发送一个消息,这样就可以在主线程中给image.view设置图片了。
LoaderResult类
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;
}
}
再来看看handler和线程池的实现
3.2.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;
private static final long KEEP_ALIVE = 10L;
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);
之所以要用到线程池是因为ImageLoader需要并发性。
3.2.2 创建Handler
再来看看handler
private Handler mMainHandler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
LoaderResult result = (LoaderResult) msg.obj;
ImageView imageView = result.imageView;
String uri = (String) imageView.getTag(TAG_KEY_URI);
if (uri.equals(result.uri)) {
imageView.setImageBitmap(result.bitmap);
} else {
Log.w(TAG, "set image bitmap,but url has changed, ignored!");
}
};
};
ImageLoader采用主线程的Looper来构造Handler对象,这就使得ImageLoader可以在非主线程中构造了。 另外为了解决由于View 复用所导致的列表错位这一问题,在给ImageView设置图片之前都会检查它的url有没有发生改变,如果发生改变就不再给他设置图片,这样就解决了列表错位的问题。
4.总结: 一个完整的图片加载框架就完成了。
package com.sihan.myview.imageLoader;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.StatFs;
import android.util.Log;
import android.util.LruCache;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import com.sihan.myview.R;
import com.sihan.myview.libcore.io.DiskLruCache;
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.MalformedURLException;
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;
import static android.content.ContentValues.TAG;
public class ImageLoader {
//磁盘缓存大小 50MB
private static final long DISK_CACHE_SIZE = 1024 * 1024 * 50;
private static final int DISK_CACHE_INDEX = 0;
private static final int IO_BUFFER_SIZE = 8 * 1024;
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;
private static final long KEEP_ALIVE = 10L;
//磁盘缓存是否创建
private boolean mIsDiskLruCacheCreated = false;
private LruCache<String, Bitmap> mMemoryCache;
private DiskLruCache mDiskLruCache;
private Context mContext;
private ImageReSizer mImageReSizer = new ImageReSizer();
private static final int TAG_KEY_URI = R.id.imageloader_uri;
private Handler mMainHandler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
LoaderResult result = (LoaderResult) msg.obj;
ImageView imageView = result.imageView;
String uri = (String) imageView.getTag(TAG_KEY_URI);
if (uri.equals(result.uri)) {
imageView.setImageBitmap(result.bitmap);
} else {
Log.w(TAG, "set image bitmap,but url has changed, ignored!");
}
};
};
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);
/**
* 构造方法
* @param context
*/
private ImageLoader(Context context){
mContext = context.getApplicationContext();
//计算最大内存
int maxMemory = (int) (Runtime.getRuntime().maxMemory()/1024);
//设置内存缓存大小为内存的1/8
int cacheSize = maxMemory/8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize){
@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{
mDiskLruCache = DiskLruCache.open(diskCacheDir,1,1,DISK_CACHE_SIZE);
mIsDiskLruCacheCreated = true;
}catch (IOException e){
e.printStackTrace();
}
}
}
/**
* 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 void addBitmapToMemoryCache(String key, Bitmap bitmap){
if (getBitmapFromMemCache(key) ==null){
mMemoryCache.put(key,bitmap);
}
}
private Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
public Bitmap loadBitmap(String uri, int reqWidth, int reqHeight){
Bitmap bitmap = getBitmapFromMemCache(uri);
if (bitmap != null){
return bitmap;
}
try{
bitmap = loadBitmapFromDiskCache(uri, reqWidth, reqHeight);
if (bitmap != null){
return bitmap;
}
bitmap = loadBitmapFromHttp(uri, reqWidth, reqHeight);
} catch (IOException e) {
e.printStackTrace();
}
if (bitmap == null && !mIsDiskLruCacheCreated){
bitmap = downloadBitmapFromUrl(uri);
}
return bitmap;
}
private void bindBitmap(final String uri, final ImageView imageView, final int reqWidth, final int reqHeight){
imageView.setTag(TAG_KEY_URI, uri);
Bitmap bitmap = getBitmapFromMemCache(uri);
if (bitmap !=null){
imageView.setImageBitmap(bitmap);
return;
}
Runnable loadBitmapTask = new Runnable() {
@Override
public void run() {
Bitmap bitmap = loadBitmap(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);
}
private Bitmap downloadBitmapFromUrl(String urlString) {
Bitmap bitmap = null;
HttpURLConnection urlConnection = null;
BufferedInputStream in = null;
try {
final URL url = new URL(urlString);
urlConnection = (HttpURLConnection) url.openConnection();
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;
}
private Bitmap loadBitmapFromDiskCache(String url, int reqWidth, int reqHeight) throws IOException {
//如果是用主线程去加载就提示
if (Looper.myLooper()==Looper.getMainLooper()){
Log.w(TAG, "loadBitmapFromDiskCache: load bitmap from UI Thread, it's not recommended!" );
}
//如果缓存不存在返回null
if (mDiskLruCache ==null){
return null;
}
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;
}
private Bitmap loadBitmapFromHttp(String url, int reqWidth, int reqHeight) throws IOException {
if (Looper.myLooper()==Looper.getMainLooper()){
Log.w(TAG, "can not visit network from UI thread" );
}
if (mDiskLruCache == null){
return null;
}
String key = hashKeyFormUrl(url);
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
if (editor != null){
OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX);
if (downloadUrlToStream(url,outputStream)){
editor.commit();
}else {
editor.abort();
}
mDiskLruCache.flush();
}
return loadBitmapFromDiskCache(url,reqWidth,reqHeight);
}
private boolean downloadUrlToStream(String urlString, OutputStream outputStream) {
HttpURLConnection urlConnection = null;
BufferedOutputStream out = null;
BufferedInputStream in = null;
try{
final URL url = new URL(urlString);
urlConnection = (HttpURLConnection) url.openConnection();
in = new BufferedInputStream(urlConnection.getInputStream());
out = new BufferedOutputStream(urlConnection.getOutputStream());
int b;
while((b=in.read()) != -1){
out.write(b);
}
return true;
} catch (IOException e) {
e.printStackTrace();
}
finally {
if (urlConnection !=null){
urlConnection.disconnect();
MyUtils.close(in);
MyUtils.close(out);
}
}
return false;
}
private String hashKeyFormUrl(String url) {
String cacheKey = null;
try{
final MessageDigest mDigest = MessageDigest.getInstance("MD5");
mDigest.update(url.getBytes());
cacheKey = bytesToHexString(mDigest.digest());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return cacheKey;
}
private String bytesToHexString(byte[] digest) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < digest.length; i++) {
String hex = Integer.toHexString(0xFF & digest[i]);
if (hex.length()==1) {
sb.append('0');
}
sb.append(hex);
}
return sb.toString();
}
/**
* 获取磁盘缓存地址
* @param context
* @param uniqueName
* @return
*/
private File getDiskCacheDir(Context context, String uniqueName) {
//获取sdk状态 是否已经挂载并且拥有可读可写权限
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);
}
/**
* 获取剩余空间
* @param path
* @return
*/
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
private 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();
}
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;
}
}
}