android图片加载各种问题小结
关于图片加载时的疑问:
1,对于一些大图片,特别加载很多大图片的时候,出现OOM怎么办?
压缩处理,官方给出的压缩算法:
public int calculateInSampleSize(BitmapFactory.Options op, int reqWidth,
int reqheight) {
int originalWidth = op.outWidth;
int originalHeight = op.outHeight;
int inSampleSize = 1;
if (originalWidth > reqWidth || originalHeight > reqheight) {
int halfWidth = originalWidth / 2;
int halfHeight = originalHeight / 2;
while ((halfWidth / inSampleSize > reqWidth)
&&(halfHeight / inSampleSize > reqheight)) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
2,对于listview中的图片,如果每从网络获取一张图片都要开启一个线程,势必会浪费掉很多资源,服务器也承受不起这么多的连接,如何解决?使用线程池,可以避免了过多线程频繁创建和销毁,有的童鞋每次总是new一个线程去执行这是非常不可取的,
好一点的用的AsyncTask类,其实内部也是用到了线程池。
3,如果每显示一张图片都要去请求一次网络,势必会浪费掉很多流量,怎样才能节省流量,这又如何解决?
使用缓存,内存缓存,本地缓存
4,如果listview在滑动的过程中都要去加载图片,势必是不科学的,如何解决?
监听listview的滑动状态,Listview滑动时不加载数据,停下来时再去加载数据
5,有时由于listview中convertView的复用,引发图片显示错位,改如何解决?
图片错位问题的本质源于我们的listview使用了缓存convertView,假设一种场景,一个listview一屏显示九个item,那么在拉出第十个item的时候,事实上该item是重复使用了第一个item,也就是说在第一个item从网络中下载图片并最终要显示的时候其实该item已经不在当前显示区域内了,此时显示的后果将是在可能在第十个item上输出图像,
这就导致了图片错位的问题。所以解决之道在于可见则显示,不可见则不显示。在ImageLoader里有个imageViews的map对象,就是用于保存当前显示区域图像对应的url集,在显示前判断处理一下即可。
最简单的解决方法就是网上说的,给 ImageView 设置一个 tag, 并预设一个图片。
图片处理考虑点:
1,图片压缩
2,图片缓存:
a,内存缓存
1,软引用:
map里面的键是用来放图片地址的,既可以是网络上的图片地址,也可以SDcard上的图片地址,map里面的值里面放的是持有软引用的Bitmap,
当然如果你要放Drawable,那也是可以的。
使用:private Map<String, SoftReference<Bitmap>> imageMap = new HashMap<String, SoftReference<Bitmap>>();
2,LruCache缓存(最近最少使用算法):
当缓存的图片达到了预先设定的值的时候,那么近期使用次数最少的图片就会被回收掉。
b,本地缓存
1,DiskLruCache
3,listview图片加载错位
1,内存缓存,使用LRUCache(最近最少使用)算法
2,使用了线程池来管理下载任务,提升多个图片加载效率
3,如何防止图片错位,设置给imageView设置tag
步骤:
1,从内存中加载图片,有则显示,没有则从本地加载图片
2,本地有则显示,没有则从网络加载图片
3,从网络加载完成,转化成bitMap之后,缓存在本地,然后缓存在内存
图片缓存流程图:
代码实现:
package com.hdc.applictionframwork.utils;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.os.Handler;
import android.support.v4.util.LruCache;
import android.util.Log;
import android.widget.ImageView;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 图片三级缓存的工具类
* Created by wK on 2016/6/18.
*/
public class ImageUtils {
//LruCache内部实现实际就是HashMap集合
private LruCache<String, Bitmap> mMemoryCache;
private Context mContext;
private Map<ImageView, String> mTags = new LinkedHashMap<ImageView, String>();
//线程池
private static ExecutorService mPool;
private Handler mHandler;
public ImageUtils(Context context){
this.mContext = context;
if(mMemoryCache == null){
//申请的内存空间占到总内存的1/8,
// (int)Runtime.getRuntime().maxMemory()/8
mMemoryCache = new LruCache<String, Bitmap>((int)Runtime.getRuntime().maxMemory()/8){
@Override
protected int sizeOf(String key, Bitmap value) {
// 判断添加进入的bitmap的占用内存的大小
return value.getRowBytes()*value.getHeight();
}
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
super.entryRemoved(evicted, key, oldValue, newValue);
Log.v("tag", "hard cache is full , push to soft cache");
}
};
}
if (mHandler == null)
{
mHandler = new Handler();
}
if (mPool == null){
synchronized(ExecutorService.class){
if(mPool == null){
//为了下载图片更加的流畅,我们用了3个线程来下载图片
mPool = Executors.newFixedThreadPool(3);
}
}
}
}
//显示图片
public void display(ImageView iv,String picUrl){
//1,从内存中获取图片
Bitmap bm = getFromMemory(picUrl);
if(bm!=null){
iv.setImageBitmap(bm);
return;
}
//2,从本地获取图片
bm = getFromLocal(picUrl);
if(bm != null){
//保存到内存
addBitmapToMemoryCache(picUrl,bm);
iv.setImageBitmap(bm);
return;
}
//3,从网络获取图片
getFromNet(iv,picUrl);
}
private void getFromNet(ImageView iv,String picUrl) {
mTags.put(iv,picUrl);
// 线程池管理
mPool.execute(new LoadImageTask(iv,picUrl));
}
public class LoadImageTask implements Runnable{
ImageView iv;
String url;
public LoadImageTask(ImageView iv, String url) {
this.iv = iv;
this.url = url;
}
@Override
public void run() {
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) new URL(url).openConnection();
conn.setConnectTimeout(5000);// 连接服务器超时时间
conn.setReadTimeout(5000);// 读取超时时间
conn.connect();// 连接服务器
if(conn.getResponseCode() == 200){
// 获取输入流
InputStream is = conn.getInputStream();
//把输入转化为bitmap
Bitmap bm = BitmapFactory.decodeStream(is);
// 存储到本地
save2Local(bm, url);
// 存储到内存
addBitmapToMemoryCache(url, bm);
String recently = mTags.get(iv);
//判断此imageView要显示的url和显示url是否相同,防止错位
if(recently.equals(url)){
mHandler.post(new Runnable() {
@Override
public void run() {
display(iv,url);
}
});
}
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if (conn != null) {
conn.disconnect();
}
}
}
}
/**
* 添加Bitmap到内存缓存
* @param key
* @param bitmap
*/
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getFromMemory(key) == null && bitmap != null) {
mMemoryCache.put(key, bitmap);
}
}
/**
* 把bitmap保存在本地
* @param bm
* @param url
*/
private void save2Local(Bitmap bm, String url) throws Exception {
File file = getCacheFile(url);
FileOutputStream out = new FileOutputStream(file);
bm.compress(Bitmap.CompressFormat.JPEG,100,out);
out.flush();
out.close();
}
/**
* 从本地获取图片
* @param picUrl
* @return
*/
private Bitmap getFromLocal(String picUrl) {
File file = getCacheFile(picUrl);
if(file.exists()){
Bitmap bm = BitmapFactory.decodeFile(file.getAbsolutePath());
// 存储到内存
mMemoryCache.put(picUrl, bm);
return bm;
}
return null;
}
/**
* 根据url从内存中获取图片
* @param picUrl
* @return
*/
private Bitmap getFromMemory(String picUrl) {
Bitmap bitmap = mMemoryCache.get(picUrl);
return bitmap;
}
/**
* 根据图片url获取存储路径
* @param picUrl
* @return
*/
public File getCacheFile(String picUrl){
//文件名加密
String name = MD5Utils.encode(picUrl);
String state = Environment.getExternalStorageState();
//判断是否有sd卡
if(Environment.MEDIA_MOUNTED.equals(state)){
//sd存在的
File dir = new File(Environment.getExternalStorageDirectory()+"/Android/data/"+mContext.getPackageName()+"/icon");
if(!dir.exists()){
dir.mkdir();
}
return new File(dir,name);
}else{
//不存在存放在缓存中
File dir = new File(mContext.getCacheDir(),"/icon");
if(!dir.exists()){
dir.mkdir();
}
return new File(dir,name);
}
}
}
使用方法:
private ImageUtils mImageUtils = new ImageUtils(context);
mImageUtils.display(holder.item_pic_iv_pic,bean.listimage);
参考链接:
Android 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅
http://blog.csdn.net/xiaanming/article/details/9825113
软引用和LruCache的区别:
http://blog.chinaunix.net/uid-26930580-id-4138306.html
BitmapFactory.Options 解决加载大图片OOM:
http://deep-fish.iteye.com/blog/2021016
http://blog.csdn.net/hjj0212/article/details/8467830
内存缓存LruCache实现原理:
http://www.cnblogs.com/liuling/p/2015-9-24-1.html
解决listview图片错位:
http://www.cnblogs.com/lesliefang/p/3619223.html
Android Listview滑动时不加载数据,停下来时加载数据,让App更优
http://blog.csdn.net/jdsjlzx/article/details/45914707