06 ImageLoader 项目源码

01 基础框架
02 请求队列
03 三级缓存
04 图片加载
05 常见问题
06 项目源码

使用 ImageLoader 示例:初始化、调用、关闭。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private void initImageLoader() {
ImageLoaderConfig config = new ImageLoaderConfig.Builder()
.setCache(new DoubleCache(this))
.setThreadCount(4)
.setLoadPolicy(new ReversePolicy())
.create();
// 初始化
ImageLoader.getInstance().init(config);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = getActivity().getLayoutInflater().inflate(
R.layout.image_item_layout, parent, false);
}
ImageView imageview = convertView.findViewById(R.id.id_img);
// 加载图片
ImageLoader.getInstance().displayImage(getItem(position), imageview);
return convertView;
}
@Override
protected void onDestroy() {
// 退出时关闭
ImageLoader.getInstance().stop();
super.onDestroy();
}

Core


ImageLoader.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public class ImageLoader {
// ImageLoader 实例
private static ImageLoader sInstance;
// 图片内存缓存
private IBitmapCache mCache = new MemoryCache();
// 网络请求队列
private RequestQueue mImageQueue;
// 图片加载配置对象
private ImageLoaderConfig mConfig;
private ImageLoader() {}
public static ImageLoader getInstance() {
if (sInstance == null) {
synchronized (ImageLoader.class) {
if (sInstance == null) {
sInstance = new ImageLoader();
}
}
}
return sInstance;
}
public void init(ImageLoaderConfig config) {
mConfig = config;
mCache = mConfig.mCache;
checkConfig();
mImageQueue = new RequestQueue(mConfig.threadCount);
mImageQueue.start();
}
private void checkConfig() {
if (mConfig == null) {
throw new RuntimeException("The config of SimpleImageLoader " +
"is Null, please call the init(ImageLoaderConfig " +
"config) method to initialize");
}
if (mConfig.loadPolicy == null) {
mConfig.loadPolicy = new SerialPolicy();
}
if (mCache == null) {
mCache = new MemoryCache();
}
}
public void stop() {
mImageQueue.stop();
}
public IBitmapCache getCache() {
return mCache;
}
public void displayImage(String uri, ImageView imageView) {
BitmapRequest request = new BitmapRequest(imageView, uri);
request.mLoadPolicy = mConfig.loadPolicy;
mImageQueue.addRequest(request);
}
}

RequestQueue.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class RequestQueue {
// 请求队列 [ Thread-safe ]
private BlockingQueue<BitmapRequest> mBitmapRequestQueue =
new PriorityBlockingQueue<>();
// 请求的序列化生成器
private AtomicInteger mSerialNumGenerator = new AtomicInteger(0);
// 默认的核心数:CPU 核心数 + 1个分发线程数
public static int DEFAULT_CORE_NUM =
Runtime.getRuntime().availableProcessors() + 1;
private int mDispatcherNum;
private RequestDispatcher[] mDispatchers = null;
protected RequestQueue() {
this(DEFAULT_CORE_NUM);
}
protected RequestQueue(int coreNum) {
mDispatcherNum = coreNum;
}
// 维护线程池
private final void startDispatchers() {
mDispatchers = new RequestDispatcher[mDispatcherNum];
for (int i = 0; i < mDispatcherNum; i++) {
mDispatchers[i] = new RequestDispatcher(mBitmapRequestQueue);
mDispatchers[i].start();
}
}
public void start() {
stop();
startDispatchers();
}
public void stop() {
if (mDispatchers != null && mDispatchers.length > 0) {
for (int i = 0; i < mDispatchers.length; i++) {
mDispatchers[i].interrupt();
}
}
}
public void addRequest(BitmapRequest request) {
if (!mBitmapRequestQueue.contains(request)) {
request.serialNum = this.generateSerialNumber();
mBitmapRequestQueue.add(request);
}
}
// 为每个请求生成一个系列号
private int generateSerialNumber() {
return mSerialNumGenerator.incrementAndGet();
}
}

RequestDispatcher.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class RequestDispatcher extends Thread {
// 网络请求队列
private BlockingQueue<BitmapRequest> mBitmapRequestQueue;
public RequestDispatcher(BlockingQueue<BitmapRequest> queue) {
mBitmapRequestQueue = queue;
}
@Override
public void run() {
try {
while (!this.isInterrupted()) {
final BitmapRequest request = mBitmapRequestQueue.take();
if (request.isCancel) {
continue;
}
Scheme scheme = Scheme.ofUri(request.imageUri);
ILoader loader = LoaderManager.getInstance().getLoader(scheme);
if (loader != null) {
loader.loadImage(request);
}
}
} catch (InterruptedException e) {
Log.i("", "### 请求分发器退出");
}
}
}

BitmapRequest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
public class BitmapRequest implements Comparable<BitmapRequest> {
private Reference<ImageView> mImageViewRef;
public String imageUri;
/**
* 是否取消该请求
*/
public boolean isCancel = false;
/**
* 请求序列号
*/
public int serialNum = 0;
/**
* 加载策略
*/
public LoadPolicy mLoadPolicy = new SerialPolicy();
public BitmapRequest(ImageView imageView, String uri) {
this.mImageViewRef = new WeakReference<>(imageView);
this.imageUri = uri;
imageView.setTag(uri);
}
public ImageView getImageView() {
return mImageViewRef.get();
}
// image view 的 tag 与 uri 是否相等
public boolean isImageViewTagValid() {
return mImageViewRef.get() != null && mImageViewRef.get().getTag().equals(imageUri);
}
public int getImageViewWidth() {
return ImageViewHelper.getImageViewWidth(mImageViewRef.get());
}
public int getImageViewHeight() {
return ImageViewHelper.getImageViewHeight(mImageViewRef.get());
}
@Override
public int compareTo(BitmapRequest another) {
return mLoadPolicy.compare(this, another);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((imageUri == null) ? 0 : imageUri.hashCode());
result = prime * result + ((mImageViewRef.get() == null) ? 0 : mImageViewRef.get().hashCode());
result = prime * result + serialNum;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
BitmapRequest other = (BitmapRequest) obj;
if (imageUri == null) {
if (other.imageUri != null)
return false;
} else if (!imageUri.equals(other.imageUri))
return false;
if (mImageViewRef == null) {
if (other.mImageViewRef != null)
return false;
} else if (!mImageViewRef.get().equals(other.mImageViewRef.get()))
return false;
if (serialNum != other.serialNum)
return false;
return true;
}
}

Cache


IBitmapCache.java

1
2
3
4
5
public interface IBitmapCache {
void put(BitmapRequest key, Bitmap value);
Bitmap get(BitmapRequest key);
void remove(BitmapRequest key);
}

MemoryCache.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class MemoryCache implements IBitmapCache {
private static final int ONE_KB = 1024;
private static final int MAX_MEMORY_PERCENT = 4;
// 图片内存缓存
private LruCache<BitmapRequest, Bitmap> mImageCache;
public MemoryCache() {
initImageCache();
}
// 初始化缓存大小:取四分之一的可用内存作为缓存。
private void initImageCache() {
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / ONE_KB);
int cacheSize = maxMemory / MAX_MEMORY_PERCENT;
mImageCache = new LruCache<BitmapRequest, Bitmap>(cacheSize) {
@Override
protected int sizeOf(BitmapRequest key, Bitmap bitmap) {
return bitmap.getRowBytes() * bitmap.getHeight() / ONE_KB;
}
};
}
public void put(BitmapRequest key, Bitmap value) {
mImageCache.put(key, value);
}
public Bitmap get(BitmapRequest key) {
return mImageCache.get(key);
}
public void remove(BitmapRequest key) {
mImageCache.remove(key);
}
}

DiskCache.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
public class DiskCache implements IBitmapCache {
// 1 MB
private static final int MB = 1024 * 1024;
// cache dir
private static final String IMAGE_DISK_CACHE = "bitmap";
// Disk LRU Cache
private DiskLruCache mDiskLruCache;
// Disk Cache Instance
private static DiskCache mDiskCache;
private String mCachePath;
private DiskCache(Context context) {
initDiskCache(context);
}
private void initDiskCache(Context context) {
try {
File cacheDir = getDiskCacheDir(context, IMAGE_DISK_CACHE);
if (!cacheDir.exists()) {
cacheDir.mkdirs();
}
mDiskLruCache = DiskLruCache
.open(cacheDir, getAppVersion(context), 1, 50 * MB);
} catch (IOException e) {
e.printStackTrace();
}
}
// 单例
public static DiskCache getDiskCache(Context context) {
if (mDiskCache == null) {
synchronized (DiskCache.class) {
if (mDiskCache == null) {
mDiskCache = new DiskCache(context);
}
}
}
return mDiskCache;
}
@Override
public synchronized Bitmap get(final BitmapRequest request) {
// 图片解析器
BitmapDecoder decoder = new BitmapDecoder() {
@Override
public Bitmap decodeBitmapWithOption(BitmapFactory.Options options) {
final InputStream inputStream = getInputStream(Md5Helper.toMD5(request.imageUri));
Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options);
IOUtil.closeQuietly(inputStream);
return bitmap;
}
};
return decoder.decodeBitmap(request.getImageViewWidth(),
request.getImageViewHeight());
}
// 将图片缓存到磁盘中
@Override
public void put(BitmapRequest request, Bitmap value) {
DiskLruCache.Editor editor;
try {
// 如果没有找到对应的缓存,则准备从网络上请求数据,并写入缓存
editor = mDiskLruCache.edit(Md5Helper.toMD5(request.imageUri));
if (editor != null) {
OutputStream outputStream = editor.newOutputStream(0);
if (writeBitmapToDisk(value, outputStream)) {
// 写入disk缓存
editor.commit();
} else {
editor.abort();
}
IOUtil.closeQuietly(outputStream);
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void remove(BitmapRequest key) {
try {
mDiskLruCache.remove(Md5Helper.toMD5(key.imageUri));
} catch (IOException e) {
e.printStackTrace();
}
}
private InputStream getInputStream(String md5) {
Snapshot snapshot;
try {
snapshot = mDiskLruCache.get(md5);
if (snapshot != null) {
return snapshot.getInputStream(0);
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private File getDiskCacheDir(Context context, String name) {
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
Log.d("", "### context : " + context + ", dir = " + context.getExternalCacheDir());
mCachePath = context.getExternalCacheDir().getPath();
} else {
mCachePath = context.getCacheDir().getPath();
}
return new File(mCachePath + File.separator + name);
}
private boolean writeBitmapToDisk(Bitmap bitmap, OutputStream outputStream) {
BufferedOutputStream bos = new BufferedOutputStream(outputStream, 8 * 1024);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);
boolean result = true;
try {
bos.flush();
} catch (IOException e) {
e.printStackTrace();
result = false;
} finally {
IOUtil.closeQuietly(bos);
}
return result;
}
private int getAppVersion(Context context) {
try {
PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(),
0);
return info.versionCode;
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return 1;
}
}

DoubleCache.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class DoubleCache implements IBitmapCache {
private MemoryCache mMemoryCache = new MemoryCache();
private DiskCache mDiskCache;
public DoubleCache(Context context) {
mDiskCache = DiskCache.getDiskCache(context);
}
@Override
public Bitmap get(BitmapRequest key) {
Bitmap value = mMemoryCache.get(key);
if (value == null) {
value = mDiskCache.get(key);
// 存到内存缓存中
saveBitmapIntoMemory(key, value);
}
return value;
}
@Override
public void put(BitmapRequest key, Bitmap bitmap) {
mMemoryCache.put(key, bitmap);
mDiskCache.put(key, bitmap);
}
@Override
public void remove(BitmapRequest key) {
mMemoryCache.remove(key);
mDiskCache.remove(key);
}
private void saveBitmapIntoMemory(BitmapRequest key, Bitmap bitmap) {
// 如果 Value 从 disk 中读取,那么存入内存缓存
if (bitmap != null) {
mMemoryCache.put(key, bitmap);
}
}
}

NoCache.java

1
2
3
4
5
6
7
8
9
10
11
12
public class NoCache implements IBitmapCache {
@Override
public Bitmap get(BitmapRequest key) {
return null;
}
@Override
public void put(BitmapRequest key, Bitmap value) {
}
@Override
public void remove(BitmapRequest key) {
}
}

Loader


ILoader.java

1
2
3
public interface ILoader {
void loadImage(BitmapRequest request);
}

AbsLoader.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public abstract class AbsLoader implements ILoader {
// 图片缓存
private IBitmapCache mCache;
@Override
public void loadImage(BitmapRequest request) {
if (null == request || null == request.getImageView()
|| TextUtils.isEmpty(request.imageUri)) {
return;
}
mCache = ImageLoader.getInstance().getCache();
// 先从内存中读取
Bitmap bitmap = mCache.get(request);
// 没有就去加载网络图片
if (null == bitmap) {
bitmap = onLoadImage(request);
cacheBitmap(request, bitmap);
}
// 通知界面更新
deliveryToUIThread(request, bitmap);
}
protected abstract Bitmap onLoadImage(BitmapRequest result);
private void cacheBitmap(BitmapRequest request, Bitmap bitmap) {
// 缓存新的图片
if (bitmap != null && mCache != null) {
synchronized (mCache) {
mCache.put(request, bitmap);
}
}
}
/**
* 更新视图控件,显示图片
* @param request ImageView
* @param bitmap Bitmap
*/
private void deliveryToUIThread(final BitmapRequest request, final Bitmap bitmap) {
final ImageView imageView = request.getImageView();
if (null != imageView) {
imageView.post(new Runnable() {
@Override
public void run() {
updateImageView(request, bitmap);
}
});
}
}
/**
* 更新ImageView
*/
private void updateImageView(BitmapRequest request, Bitmap result) {
ImageView imageView = request.getImageView();
if (result != null && request.isImageViewTagValid()) {
imageView.setImageBitmap(result);
}
}
}

LocalLoader.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class LocalLoader extends AbsLoader {
@Override
public Bitmap onLoadImage(BitmapRequest request) {
final String imagePath = Uri.parse(request.imageUri).getPath();
final File imgFile = new File(imagePath);
if (!imgFile.exists()) {
return null;
}
// 加载图片
BitmapDecoder decoder = new BitmapDecoder() {
@Override
public Bitmap decodeBitmapWithOption(Options options) {
return BitmapFactory.decodeFile(imagePath, options);
}
};
return decoder.decodeBitmap(request.getImageViewWidth(),
request.getImageViewHeight());
// return BitmapFactory.decodeFile(imagePath);
}
}

UrlLoader.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class UrlLoader extends AbsLoader {
@Override
protected Bitmap onLoadImage(BitmapRequest request) {
if (null == request || TextUtils.isEmpty(request.imageUri)) {
return null;
}
Bitmap bitmap = null;
try {
URL url = new URL(request.imageUri);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
bitmap = BitmapFactory.decodeStream(conn.getInputStream());
conn.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
}

NullLoader.java

1
2
3
4
5
6
7
8
public class NullLoader extends AbsLoader {
@Override
public Bitmap onLoadImage(BitmapRequest requestBean) {
Log.e(NullLoader.class.getSimpleName(), "### wrong schema, " +
"your image uri is : " + requestBean.imageUri);
return null;
}
}

LoaderManager.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class LoaderManager {
private Map<Scheme, ILoader> mLoaderMap = new HashMap<>();
private ILoader mNullLoader = new NullLoader();
private static LoaderManager INSTANCE;
private LoaderManager() {
register(Scheme.HTTP, new UrlLoader());
register(Scheme.HTTPS, new UrlLoader());
register(Scheme.FILE, new LocalLoader());
}
public static LoaderManager getInstance() {
if (INSTANCE == null) {
synchronized (LoaderManager.class) {
if (INSTANCE == null) {
INSTANCE = new LoaderManager();
}
}
}
return INSTANCE;
}
// 注册 Loader
public final synchronized void register(Scheme schema, ILoader loader) {
mLoaderMap.put(schema, loader);
}
// 根据 schema 获取对应的 Loader
public ILoader getLoader(Scheme schema) {
if (mLoaderMap.containsKey(schema)) {
return mLoaderMap.get(schema);
}
return mNullLoader;
}
}

Policy


LoadPolicy.java

1
2
3
public interface LoadPolicy {
int compare(BitmapRequest request1, BitmapRequest request2);
}

SerialPolicy.java

1
2
3
4
5
6
public class SerialPolicy implements LoadPolicy {
@Override
public int compare(BitmapRequest request1, BitmapRequest request2) {
return request1.serialNum - request2.serialNum;
}
}

ReversePolicy.java

1
2
3
4
5
6
public class ReversePolicy implements LoadPolicy {
@Override
public int compare(BitmapRequest request1, BitmapRequest request2) {
return request2.serialNum - request1.serialNum;
}
}

Config


BitmapDecoder.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// 封装先加载图片 bound,计算出 inSmallSize 之后再加载图片的逻辑操作
public abstract class BitmapDecoder {
/**
* @param options 配置
* @return Bitmap
*/
public abstract Bitmap decodeBitmapWithOption(Options options);
/**
* @param width 图片的目标宽度
* @param height 图片的目标高度
* @return Bitmap
*/
public final Bitmap decodeBitmap(int width, int height) {
// 如果请求原图,则直接加载原图
if (width <= 0 || height <= 0) {
return decodeBitmapWithOption(null);
}
// 1、获取只加载Bitmap宽高等数据的Option, 即设置options.inJustDecodeBounds = true;
BitmapFactory.Options options = new BitmapFactory.Options();
// 设置为true,表示解析Bitmap对象,该对象不占内存
options.inJustDecodeBounds = true;
// 2、通过options加载bitmap,此时返回的bitmap为空,数据将存储在options中
decodeBitmapWithOption(options);
// 3、计算缩放比例, 并且将options.inJustDecodeBounds设置为false;
configBitmapOptions(options, width, height);
// 4、通过options设置的缩放比例加载图片
return decodeBitmapWithOption(options);
}
// 加载原图
public Bitmap decodeOriginBitmap() {
return decodeBitmapWithOption(null);
}
protected void configBitmapOptions(Options options, int width, int height) {
// 设置缩放比例
options.inSampleSize = computeInSmallSize(options, width, height);
Log.d("", "$## inSampleSize = " + options.inSampleSize
+ ", width = " + width + ", height= " + height);
// 图片质量
options.inPreferredConfig = Config.RGB_565;
// 设置为false,解析Bitmap对象加入到内存中
options.inJustDecodeBounds = false;
options.inPurgeable = true;
options.inInputShareable = true;
}
private int computeInSmallSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
final float totalPixels = width * height;
final float totalReqPixelsCap = reqWidth * reqHeight * 2;
while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
inSampleSize++;
}
}
return inSampleSize;
}
}

Scheme.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public enum Scheme {
HTTP("http"), HTTPS("https"), FILE("file"), CONTENT("content"),
ASSETS("assets"), DRAWABLE("drawable"), UNKNOWN("");
private String scheme;
private String uriPrefix;
Scheme(String scheme) {
this.scheme = scheme;
uriPrefix = scheme + "://";
}
public static Scheme ofUri(String uri) {
if (uri != null) {
for (Scheme s : values()) {
if (s.belongsTo(uri)) {
return s;
}
}
}
return UNKNOWN;
}
private boolean belongsTo(String uri) {
return uri.toLowerCase(Locale.US).startsWith(uriPrefix);
}
public String wrap(String path) {
return uriPrefix + path;
}
public String crop(String uri) {
if (!belongsTo(uri)) {
throw new IllegalArgumentException(String.format("URI [%1$s] " +
"doesn't have expected scheme [%2$s]", uri, scheme));
}
return uri.substring(uriPrefix.length());
}
}

Config


ImageLoaderConfig.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
public class ImageLoaderConfig {
// 图片缓存配置对象
IBitmapCache mCache = new MemoryCache();
// 图片加载中显示的图片 id
int loadingImageId;
// 加载失败时显示的图片 id
int loadFailedImageId;
// 图片加载策略
LoadPolicy loadPolicy;
// 线程数量,默认为 CPU 数量 + 1
int threadCount = Runtime.getRuntime().availableProcessors() + 1;
private ImageLoaderConfig() {}
/**
* 配置类的 Builder
*/
public static class Builder {
// 图片缓存配置对象
IBitmapCache mCache = new MemoryCache();
// 图片加载中显示的图片 id
int loadingImageId;
// 加载失败时显示的图片 id
int loadFailedImageId;
// 图片加载策略
LoadPolicy loadPolicy;
// 线程数量,默认为 CPU 数量 + 1
int threadCount = Runtime.getRuntime().availableProcessors() + 1;
// 设置缓存
public Builder setCache(IBitmapCache cache) {
this.mCache = cache;
return this;
}
// 设置正在加载中显示的图片
public Builder setLoadingImageId(int loadingImageId) {
this.loadingImageId = loadingImageId;
return this;
}
// 设置要加载的图片失败时显示的图片
public Builder setLoadFailedImageId(int loadingFailedImageId) {
this.loadFailedImageId = loadingFailedImageId;
return this;
}
// 设置加载策略
public Builder setLoadPolicy(LoadPolicy loadPolicy) {
this.loadPolicy = loadPolicy;
return this;
}
// 设置线程数量
public Builder setThreadCount(int threadCount) {
this.threadCount = threadCount;
return this;
}
void applyConfig(ImageLoaderConfig config) {
config.mCache = this.mCache;
config.loadingImageId = this.loadingImageId;
config.loadFailedImageId = this.loadFailedImageId;
config.loadPolicy = this.loadPolicy;
config.threadCount = this.threadCount;
}
public ImageLoaderConfig create() {
ImageLoaderConfig config = new ImageLoaderConfig();
applyConfig(config);
return config;
}
}
}

Utils


BitmapDecoder.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/**
* 封装先加载图片 bound,计算出 inSmallSize 之后再加载图片的逻辑操作
*/
public abstract class BitmapDecoder {
public abstract Bitmap decodeBitmapWithOption(Options options);
public final Bitmap decodeBitmap(int width, int height) {
// 如果请求原图,则直接加载原图
if (width <= 0 || height <= 0) {
return decodeBitmapWithOption(null);
}
// 1、获取只加载Bitmap宽高等数据的Option, 即设置options.inJustDecodeBounds = true;
BitmapFactory.Options options = new BitmapFactory.Options();
// 设置为true,表示解析Bitmap对象,该对象不占内存
options.inJustDecodeBounds = true;
// 2、通过options加载bitmap,此时返回的bitmap为空,数据将存储在options中
decodeBitmapWithOption(options);
// 3、计算缩放比例, 并且将options.inJustDecodeBounds设置为false;
configBitmapOptions(options, width, height);
// 4、通过options设置的缩放比例加载图片
return decodeBitmapWithOption(options);
}
// 加载原图
public Bitmap decodeOriginBitmap() {
return decodeBitmapWithOption(null);
}
protected void configBitmapOptions(Options options, int width, int height) {
// 设置缩放比例
options.inSampleSize = computeInSmallSize(options, width, height);
Log.d("", "$## inSampleSize = " + options.inSampleSize
+ ", width = " + width + ", height= " + height);
// 图片质量
options.inPreferredConfig = Config.RGB_565;
// 设置为false,解析Bitmap对象加入到内存中
options.inJustDecodeBounds = false;
options.inPurgeable = true;
options.inInputShareable = true;
}
private int computeInSmallSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// Calculate ratios of height and width to requested height and
// width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
final float totalPixels = width * height;
final float totalReqPixelsCap = reqWidth * reqHeight * 2;
while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
inSampleSize++;
}
}
return inSampleSize;
}
}

ImageViewHelper.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public class ImageViewHelper {
// todo : 配置类中
private static int DEFAULT_WIDTH = 200;
private static int DEFAULT_HEIGHT = 200;
public static int getImageViewWidth(ImageView imageView) {
if (imageView != null) {
final ViewGroup.LayoutParams params = imageView.getLayoutParams();
int width = 0;
if (params != null && params.width != ViewGroup.LayoutParams.WRAP_CONTENT) {
width = imageView.getWidth(); // Get actual image width
}
if (width <= 0 && params != null) {
width = params.width; // Get layout width parameter
}
if (width <= 0) {
width = getImageViewFieldValue(imageView, "mMaxWidth");
}
return width;
}
return DEFAULT_WIDTH;
}
public static int getImageViewHeight(ImageView imageView) {
if (imageView != null) {
final ViewGroup.LayoutParams params = imageView.getLayoutParams();
int height = 0;
if (params != null
&& params.height != ViewGroup.LayoutParams.WRAP_CONTENT) {
height = imageView.getHeight(); // Get actual image height
}
if (height <= 0 && params != null) {
// Get layout height parameter
height = params.height;
}
if (height <= 0) {
height = getImageViewFieldValue(imageView, "mMaxHeight");
}
return height;
}
return DEFAULT_HEIGHT;
}
private static int getImageViewFieldValue(Object object, String fieldName) {
int value = 0;
try {
Field field = ImageView.class.getDeclaredField(fieldName);
field.setAccessible(true);
int fieldValue = (Integer) field.get(object);
if (fieldValue > 0 && fieldValue < Integer.MAX_VALUE) {
value = fieldValue;
}
} catch (Exception e) {
Log.e("", e.getMessage());
}
return value;
}
}

Md5Helper.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class Md5Helper {
private static MessageDigest mDigest = null;
static{
try {
mDigest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
// 对 key 进行 MD5 加密,如果无 MD5 加密算法,
// 则直接使用key对应的hash值。
public static String toMD5(String key) {
String cacheKey;
//获取MD5算法失败时,直接使用key对应的hash值
if ( mDigest == null ) {
return String.valueOf(key.hashCode());
}
mDigest.update(key.getBytes());
cacheKey = bytesToHexString(mDigest.digest());
return cacheKey;
}
private static 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();
}
}

IOUtil.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class IOUtil {
private IOUtil() {}
/**
* 关闭IO
* @param closeables closeables
*/
public static void close(Closeable... closeables) {
if (closeables == null) return;
for (Closeable closeable : closeables) {
if (closeable != null) {
try {
closeable.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 关闭IO,不输出日志
* @param closeables closeables
*/
public static void closeQuietly(Closeable... closeables) {
if (closeables == null) return;
for (Closeable closeable : closeables) {
if (closeable != null) {
try { closeable.close(); } catch (IOException e) {}
}
}
}
}