// Find the correct scale value. It should be the power of 2. final int REQUIRED_SIZE = 70; int width_tmp = o.outWidth, height_tmp = o.outHeight; int scale = 1; while (true) { if (width_tmp / 2 < REQUIRED_SIZE || height_tmp /2 < REQUIRED_SIZE) break; width_tmp /= 2; height_tmp /= 2; scale *= 2; }
2. 缓存(内存和磁盘结合)
3. decode等费时的操作不要放到主线程里
每个文件只需要decode一次就可以了,decode出来以后,就把小图存放到sdcard上。
磁盘缓存的话,可以用的HashMap<String, String>,key为文件路径,value为decode出来的小图存放的文件路径。
内存缓存的话, 可以用个LinkedHashMap, 采用FIFO(先进先出的原则), 将最近的N的小图保存起来。一般getview中流程是这样,以一个图片为例:
(实际图片的路径为fp, decode出来的小图保存到sdcard上的路径为cp)
1. 判断内存缓存中是否已经缓存了,如果缓存了,直接取出来用。
2. 如果内存中没有缓存,查询sdcard上是否缓存,如果缓存了,取出来显示,并且添加到内存缓存中。
3. 如果内存缓存和sdcard上都没有缓存,那么就从原始图片decode, 然后把decode出来的小图保存到内存缓存和sdcard缓存中。这个只是我个人的理解,有可能有不周到或者不对的地方,你做个参考的。
更权威的做法,请参考google官方文档:
http://developer.android.com/training/displaying-bitmaps/index.html
(我还没来得及看这个:))
不太懂这个部分,请见谅~
具体的还是去看看google的那个官方文档吧。
http://developer.android.com/training/displaying-bitmaps/index.html
讲解很详细, 包括图片的缓存, 怎么效率的加载一张大图,怎么在非主线程里面加载图片。要学习android的话,建议你仔细读读android的文档advanced training.
http://developer.android.com/training/advanced.html
1、对于小于100K的图,直接显示,不再decode;
2、大于100K的图,先显示一个默认的小图片,再启用一个线程,将图片控件对像以及图片路径传递过去,由线程处理。
/**
* 先从内存缓存中查找,若没有再开线程,
* 从文件缓存中查找都没有,则从指定的url中查找,并对bitmap进行处理,最后对UI进行更新操作
* @author Treasure
*
*/
public class ImageLoader
{
MemoryCache memoryCache = new MemoryCache();
FileCache fileCache;
private Map<ImageView, String> imageViews = Collections
.synchronizedMap(new WeakHashMap<ImageView, String>());
// 线程池
ExecutorService executorService;
public ImageLoader(Context context)
{
fileCache = new FileCache(context);
executorService = Executors.newFixedThreadPool(5);
}
// 当进入listview时默认的图片,可换成你自己的默认图片
final int stub_id = R.drawable.none_logo;
// 最主要的方法
public void displayImage(String url, ImageView imageView)
{
imageViews.put(imageView, url);
// 先从内存缓存中查找
Bitmap bitmap = memoryCache.get(url);
if (bitmap != null)
imageView.setImageBitmap(bitmap);
else
{
// 若没有的话,则开启新线程加载图片
queuePhoto(url, imageView);
imageView.setImageResource(stub_id);
}
}
private void queuePhoto(String url, ImageView imageView)
{
PhotoToLoad p = new PhotoToLoad(url, imageView);
executorService.submit(new PhotosLoader(p));
}
private Bitmap getBitmap(String url)
{
File f = fileCache.getFile(url);
// 先从文件缓存中查找是否有
Bitmap b = decodeFile(f);
if (b != null)
{
return b;
}
// 最后从指定的url中下载图片
try
{
Bitmap bitmap = null;
URL imageUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection)imageUrl
.openConnection();
conn.setConnectTimeout(30 * 1000);
conn.setReadTimeout(30 * 1000);
conn.setInstanceFollowRedirects(true);
InputStream is = conn.getInputStream();
OutputStream os = new FileOutputStream(f);
copyStream(is, os);
os.close();
bitmap = decodeFile(f);
return bitmap;
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
}
/**
* decode 这个图片并且按比例缩放以减少内存消耗,虚拟机对每张图片的缓存大小也是有限制的
* @param f
* @return
*/
private Bitmap decodeFile(File f)
{
try
{
// decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f), null, o);
// Find the correct scale value. It should be the power of 2.
final int REQUIRED_SIZE = 70;
int width_tmp = o.outWidth, height_tmp = o.outHeight;
int scale = 1;
while (true)
{
if (width_tmp / 2 < REQUIRED_SIZE
|| height_tmp /2 < REQUIRED_SIZE)
break;
width_tmp /= 2;
height_tmp /= 2;
scale *= 2;
}
// decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
}
catch (FileNotFoundException e)
{
}
return null;
}
/**
* Task for the queue
* @author Treasure
*
*/
private class PhotoToLoad
{
public String url;
public ImageView imageView;
public PhotoToLoad(String u, ImageView i)
{
url = u;
imageView = i;
}
}
class PhotosLoader implements Runnable
{
PhotoToLoad photoToLoad;
PhotosLoader(PhotoToLoad photoToLoad)
{
this.photoToLoad = photoToLoad;
}
@Override
public void run()
{
if (imageViewReused(photoToLoad))
return;
Bitmap bmp = getBitmap(photoToLoad.url);
memoryCache.put(photoToLoad.url, bmp);
if (imageViewReused(photoToLoad))
return;
BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad);
// 更新的操作放在UI线程中
Activity a = (Activity)photoToLoad.imageView.getContext();
a.runOnUiThread(bd);
}
}
/**
* 防止图片错位
* @param photoToLoad
* @return
*/
boolean imageViewReused(PhotoToLoad photoToLoad)
{
String tag = imageViews.get(photoToLoad.imageView);
if (tag == null || !tag.equals(photoToLoad.url))
return true;
return false;
}
class BitmapDisplayer implements Runnable
{
Bitmap bitmap;
PhotoToLoad photoToLoad;
public BitmapDisplayer(Bitmap b, PhotoToLoad p)
{
bitmap = b;
photoToLoad = p;
}
@Override
public void run()
{
if (imageViewReused(photoToLoad))
return;
if (bitmap != null)
photoToLoad.imageView.setImageBitmap(bitmap);
else
photoToLoad.imageView.setImageResource(stub_id);
}
}
public void clearCache()
{
memoryCache.clear();
fileCache.clear();
}
public static void copyStream(InputStream is, OutputStream os)
{
final int buffer_size = 1024;
try
{
byte[] bytes = new byte[buffer_size];
for (; ;)
{
int count = is.read(bytes, 0, buffer_size);
if (count == -1)
break;
os.write(bytes, 0, count);
}
}
catch (Exception e)
{
}
}
}
在自定义的适配器中的getView()方法中调用:// imageLoader是ImageLoader类的引用
// itemBean.getLogoAddress()获得网络的Url
// imageView是ImageView控件
imageLoader.displayImage(itemBean.getLogoAddress(), imageView);
MemoryCache是什么呢?请教一下,SoftReference的生命周期是多少呢?我想多个listview共用图片缓存要怎么做呢?
不好意思,代码没有发全,在这补上,不知道楼主解决了吗?SoftReference的生命周期可以参考:http://blog.csdn.net/feng88724/article/details/6590064MemoryCache.java
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;import android.graphics.Bitmap;/**
* 内存缓存类(一)
* @author Treasure
*/
public class MemoryCache
{
// private static final String TAG = "MemoryCache";
// 放入缓存时是个同步操作
// LinkedHashMap构造方法的最后一个参数 true 代表这个 map 里的元素将按照最近使用次数由少到多排列,即 LRU
// 这样的好处是如果要将缓存中的元素替换,则先遍历出最近最少使用的元素来替换以提高效率
private Map<String, Bitmap> cache = Collections
.synchronizedMap(new LinkedHashMap<String, Bitmap>(10, 1.5f, true));
// 缓存中图片所占用的字节,初始0,将通过此变量严格控制缓存所占用的堆内存
private long size = 0; // current allocated size
// 缓存只能占用的最大堆内存
private long limit = 1000000; // max memory in bytes
public MemoryCache()
{
// use 25% of available heap size
setLimit(Runtime.getRuntime().maxMemory() / 4);
}
public void setLimit(long new_limit)
{
limit = new_limit;
// Log.i(TAG, "MemoryCache will use up to " + limit / 1024. / 1024. + "MB");
}
public Bitmap get(String id)
{
try
{
if (!cache.containsKey(id))
return null;
return cache.get(id);
}
catch (NullPointerException e)
{
return null;
}
}
public void put(String id, Bitmap bitmap)
{
try
{
if (cache.containsKey(id))
size -= getSizeInBytes(cache.get(id));
cache.put(id, bitmap);
size += getSizeInBytes(bitmap);
checkSize();
}
catch (Throwable th)
{
th.printStackTrace();
}
}
/**
* 严格控制堆内存,如果超过将首先替换最近最少使用的那个图片缓存
*/
private void checkSize()
{
// Log.i(TAG, "cache size = " + size + ", length = " + cache.size());
if (size > limit)
{
// 先遍历最近最少使用的元素
Iterator<Entry<String, Bitmap>> iter = cache.entrySet().iterator();
while (iter.hasNext())
{
Entry<String, Bitmap> entry = iter.next();
size -= getSizeInBytes(entry.getValue());
iter.remove();
if (size <= limit)
break;
}
// Log.i(TAG, "Clean cache. New size " + cache.size());
}
}
public void clear()
{
cache.clear();
}
/**
* 图片占用的内存
* @param bitmap
* @return
*/
long getSizeInBytes(Bitmap bitmap)
{
if (bitmap == null)
return 0;
return bitmap.getRowBytes() * bitmap.getHeight();
}
}
FileCache.javaimport java.io.File;import android.content.Context;/**
* 文件缓存类
* @author Treasure
*
*/
public class FileCache
{
private File cacheDir;
public FileCache(Context context)
{
// 如果有 SD 卡则在 SD 卡中建一个 LazyList 的目录存放缓存的图片
// 没有 SD 卡就放在系统的缓存目录中
if (android.os.Environment.getExternalStorageState().equals(
android.os.Environment.MEDIA_MOUNTED))
cacheDir = new File(
android.os.Environment.getExternalStorageDirectory(), "LazyList");
else
cacheDir = context.getCacheDir();
if (!cacheDir.exists())
cacheDir.mkdirs();
}
public File getFile(String url)
{
// 将 url 的 hashcode 作为缓存的文件名
String filename = String.valueOf(url.hashCode());
// Another possible solution
// String filename = URLEncoder.encode(url);
File f = new File(cacheDir, filename);
return f;
}
public void clear()
{
File[] files = cacheDir.listFiles();
if (files == null)
return;
for (File f : files)
f.delete();
}
}