简易的Android网络图片加载器
在项目开发中,我们加载图片一般使用的是第三方库,比如Glide,在闲暇之余突发奇想,自己动手实现一个简单的Android网络图片加载器。
首先定义API,API的定义应该简单易用,比如
imageLoader.displayImage(imageView,imagePath);
其次应该支持缓存。缓存一般是指三级缓存,先定义一个入口类ImageLoader
public ImageLoader(Activity activity) {
this.activity = activity;
memoryCache = new MemoryCache();
diskCache = new DiskCache(activity);
netCache = new NetCache(activity,memoryCache,diskCache);
}
在初始化的时候就初始化内存缓存,磁盘缓存,网络缓存三个变量,然后定义加载方法:
public void displayImage(final ImageView imageView, String url,int placeholder){
imageView.setTag(url);
imageView.setImageResource(placeholder);
Bitmap bitmap;
bitmap = memoryCache.getBitmap(url);
if(bitmap != null){
imageView.setImageBitmap(bitmap);
Log.i(TAG, "从内存中获取图片");
return;
}
bitmap = diskCache.getBitmap(url);
if(bitmap != null){
imageView.setImageBitmap(bitmap);
memoryCache.setBitmap(url,bitmap);
Log.i(TAG, "从磁盘中获取图片");
return;
}
netCache.getBitmap(imageView,url);
}
首先将图片地址设置给ImageView的tag,防止因为ImageView复用导致图片错乱的问题。然后设置一个占位图防止图片加载过慢ImageVIew显示白板
三级缓存中从内存中加载缓存信息是最快的,所以第一步从内存缓存中查找,如果找到了就直接设置给ImageView,否则继续从磁盘缓存中查找,找到了就显示,最后实在找不到就从网络下载图片
内存缓存
public class MemoryCache {
private LruCache<String,Bitmap> lruCache;
public MemoryCache() {
long maxMemory = Runtime.getRuntime().maxMemory() / 8;
lruCache = new LruCache<String,Bitmap>((int) maxMemory){
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount();
}
};
}
public Bitmap getBitmap(String url) {
return lruCache.get(url);
}
public void setBitmap(String url,Bitmap bitmap) {
lruCache.put(url,bitmap);
}
}
内存缓存比较简单,只需要将加载过的图片放入内存,然后下次加载直接获取,由于内存大小有限制,所以这里使用了LruCache算法保证缓存不会无限制增长。
磁盘缓存
对于已经缓存在磁盘上的文件,就不需要在从网络下载了,直接从磁盘读取。
public Bitmap getBitmap(String url) {
FileInputStream is;
String cacheUrl = Md5Utils.md5(url);
File parentFile = new File(Values.PATH_CACHE);
File file = new File(parentFile,cacheUrl);
if(file.exists()){
try {
is = new FileInputStream(file);
Bitmap bitmap = decodeSampledBitmapFromFile(file.getAbsolutePath());
is.close();
return bitmap;
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
考虑到多图加载的时候,如果图片太大容易OOM,所以需要对加载的图片稍作处理
public Bitmap decodeSampledBitmapFromFile(String pathName){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(pathName,options);
options.inSampleSize = calculateInSampleSize(options)*2;
options.inJustDecodeBounds = false;
options.inPreferredConfig = Bitmap.Config.RGB_565;
return BitmapFactory.decodeFile(pathName,options);
}
这里降低了图片的采样,对图片的质量进行了压缩
对于本地没有缓存的图片,需要从网络下载,当获取到图片流之后,保存在本地
public void saveBitmap(InputStream inputStream, String url) throws IOException {
String cacheUrl = Md5Utils.md5(url);
File parentFile = new File(Values.PATH_CACHE);
if(!parentFile.exists()){
parentFile.mkdirs();
}
FileOutputStream fos = new FileOutputStream(new File(parentFile,cacheUrl));
byte[] bytes = new byte[1024];
int index = 0;
while ((index = inputStream.read(bytes))!=-1){
fos.write(bytes,0,index);
fos.flush();
}
inputStream.close();
fos.close();
}
为了防止图片url带一些非法字符导致创建文件失败,所以对url进行了md5处理
网络缓存
这里比较简单,直接从服务器加载图片信息就可以了,访问网络使用了OkHttp
public void getBitmap(final ImageView imageView, final String url) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder() .get().url(url).build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
imageView.setImageResource(R.mipmap.ic_error);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
InputStream inputStream = response.body().byteStream();
diskCache.saveBitmap(inputStream, url);
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
if (url != null && url.equals(imageView.getTag())) {
Bitmap bitmap = diskCache.getBitmap(url);
memoryCache.setBitmap(url, bitmap);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
} else {
imageView.setImageResource(R.mipmap.ic_error);
}
} else {
imageView.setImageResource(R.mipmap.ic_place);
}
}
});
}
});
}
当获取到图片后,分别放入磁盘和内存缓存起来
使用
最后直接在需要加载图片的地方调用
new ImageLoader(activity).displayImage(imageView,path)
作者:晚来天欲雪_
来源:juejin.cn/post/7088693420109070373