zoukankan      html  css  js  c++  java
  • android listview 滚动时异步加载图片的问题

    LoadImage.java

    package com.gowin.cach;

    import java.util.Collection;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import android.graphics.Bitmap;
    import android.os.Handler;
    import android.os.Message;
    import android.widget.ImageView;

    public class LoadImage {
    private ExecutorService executorService; // 固定五个线程来
    private ImageMemoryCache memoryCache;// 内存缓存
    private ImageFileCache fileCache;// 文件缓存
    private Map<String, ImageView> taskMap;// 存放任务

    public LoadImage() {
    executorService = Executors.newFixedThreadPool(10);

    memoryCache = new ImageMemoryCache();
    fileCache = new ImageFileCache();
    taskMap = new HashMap<String, ImageView>();
    }

    public void addTask(String url, ImageView img) {
    Bitmap bitmap=memoryCache.getBitmapFromCache(url);
    if(bitmap!=null)
    {
    img.setImageBitmap(bitmap);
    }else
    {
    synchronized (taskMap) {
    taskMap.put(Integer.toString(img.hashCode()), img);
    }
    }
    }

    public void doTask() {
    synchronized (taskMap) {
    Collection<ImageView> con = taskMap.values();
    for (ImageView i : con) {
    if (i != null) {
    if (i.getTag() != null) {
    loadImage((String) i.getTag(), i);
    }
    }
    }
    taskMap.clear();
    }
    }

    private void loadImage(String url, ImageView img) {
    /*** 加入新的任务 ***/
    executorService.submit(new TaskWithResult(new TaskHandler(url, img),
    url));
    }

    /*** 获得一个图片,从三个地方获取,首先是内存缓存,然后是文件缓存,最后从网络获取 ***/
    private Bitmap getBitmap(String url) {
    // 从内存缓存中获取图片
    Bitmap result;
    result = memoryCache.getBitmapFromCache(url);
    if (result == null) {
    // 文件缓存中获取
    result = fileCache.getImage(url);
    if (result == null) {
    // 从网络获取
    result = ImageGetForHttp.downloadBitmap(url);
    if (result != null) {
    memoryCache.addBitmapToCache(url, result);
    fileCache.saveBmpToSd(result, url);
    }
    } else {
    // 添加到内存缓存
    memoryCache.addBitmapToCache(url, result);
    }
    }
    return result;
    }

    /*** 完成消息 ***/
    private class TaskHandler extends Handler {
    String url;
    ImageView img;

    public TaskHandler(String url, ImageView img) {
    this.url = url;
    this.img = img;
    }

    @Override
    public void handleMessage(Message msg) {
    /*** 查看imageview需要显示的图片是否被改变 ***/
    if (img.getTag().equals(url)) {
    if (msg.obj != null) {
    Bitmap bitmap = (Bitmap) msg.obj;
    img.setImageBitmap(bitmap);
    }
    }
    }
    }

    /*** 子线程任务 ***/
    private class TaskWithResult implements Callable<String> {
    private String url;
    private Handler handler;

    public TaskWithResult(Handler handler, String url) {
    this.url = url;
    this.handler = handler;
    }

    @Override
    public String call() throws Exception {
    Message msg = new Message();
    msg.obj = getBitmap(url);
    if (msg.obj != null) {
    handler.sendMessage(msg);
    }
    return url;
    }

    }
    }

    ImageMemoryCache.java

    package com.gowin.cach;

    import java.lang.ref.SoftReference;
    import java.util.HashMap;
    import java.util.LinkedHashMap;
    import java.util.concurrent.ConcurrentHashMap;

    import android.graphics.Bitmap;
    /****
    *内存中的缓存
    ***
    */
    public class ImageMemoryCache {
    private static final int HARD_CACHE_CAPACITY = 30;
    private HashMap<String, Bitmap> mHardBitmapCache ;
    private final static ConcurrentHashMap<String, SoftReference<Bitmap>> mSoftBitmapCache =
    new ConcurrentHashMap<String, SoftReference<Bitmap>>(HARD_CACHE_CAPACITY / 2);

    public ImageMemoryCache()
    {
    mHardBitmapCache =
    new LinkedHashMap<String, Bitmap>(HARD_CACHE_CAPACITY / 2, 0.75f, true) {
    /**
    *
    */
    private static final long serialVersionUID = 1L;

    @Override
    protected boolean removeEldestEntry(LinkedHashMap.Entry<String, Bitmap> eldest) {
    if (size() > HARD_CACHE_CAPACITY) {
    // Entries push-out of hard reference cache are transferred to soft reference cache
    mSoftBitmapCache.put(eldest.getKey(), new SoftReference<Bitmap>(eldest.getValue()));
    return true;
    } else
    return false;
    }
    };

    }

    /**
    * 从缓存中获取图片
    */
    public Bitmap getBitmapFromCache(String url) {
    // 先从mHardBitmapCache缓存中获取
    synchronized (mHardBitmapCache) {
    final Bitmap bitmap =mHardBitmapCache.get(url);
    if (bitmap != null) {
    //如果找到的话,把元素移到linkedhashmap的最前面,从而保证在LRU算法中是最后被删除
    mHardBitmapCache.remove(url);
    mHardBitmapCache.put(url,bitmap);
    return bitmap;
    }
    }
    //如果mHardBitmapCache中找不到,到mSoftBitmapCache中找
    SoftReference<Bitmap>bitmapReference = mSoftBitmapCache.get(url);
    if (bitmapReference != null) {
    final Bitmap bitmap =bitmapReference.get();
    if (bitmap != null) {
    //将图片移回硬缓存
    mHardBitmapCache.put(url, bitmap);
    mSoftBitmapCache.remove(url);
    return bitmap;
    } else {
    mSoftBitmapCache.remove(url);
    }
    }
    return null;
    }
    /***添加图片到缓存***/
    public void addBitmapToCache(String url, Bitmap bitmap) {
    if (bitmap != null) {
    synchronized (mHardBitmapCache) {
    mHardBitmapCache.put(url, bitmap);
    }
    }
    }
    }

    ImageFileCache.java

    package com.gowin.cach;

    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.util.Arrays;
    import java.util.Comparator;

    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.os.Environment;
    import android.os.StatFs;
    import android.util.Log;

    public class ImageFileCache {
    private static final String CACHDIR = "data/gowin/imgCach";
    private static final String WHOLESALE_CONV = ".cach";
    /**过期时间3天**/
    private static final long mTimeDiff = 3 * 24 * 60 * 60 * 1000;
    public ImageFileCache()
    {
    //清理文件缓存
    removeCache(getDirectory());
    }

    public Bitmap getImage(final String url) {
    final String path = getDirectory() + "/" + convertUrlToFileName(url);
    File file = new File(path);
    if (file.exists()) {
    Bitmap bmp=BitmapFactory.decodeFile(path);
    if(bmp==null)
    {
    file.delete();
    }else
    {
    updateFileTime(path);
    return bmp;
    }
    }
    return null;
    }

    /***缓存空间大小****/
    private static final int FREE_SD_SPACE_NEEDED_TO_CACHE=10;
    public void saveBmpToSd(Bitmap bm, String url) {
    if (bm == null) {
    //需要保存的是一个空值
    return;
    }
    //判断sdcard上的空间
    if (FREE_SD_SPACE_NEEDED_TO_CACHE >freeSpaceOnSd()) {
    //SD空间不足
    return;
    }
    String filename =convertUrlToFileName(url);
    String dir = getDirectory();
    File file = new File(dir +"/" + filename);
    try {
    file.createNewFile();
    OutputStream outStream = new FileOutputStream(file);
    bm.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
    outStream.flush();
    outStream.close();

    } catch (FileNotFoundException e) {
    Log.w("ImageFileCache","FileNotFoundException");
    } catch (IOException e) {
    Log.w("ImageFileCache","IOException");
    }
    }


    private static final int CACHE_SIZE=10;

    // 清理缓存
    /**
    * 计算存储目录下的文件大小,
    * 当文件总大小大于规定的CACHE_SIZE或者sdcard剩余空间小于FREE_SD_SPACE_NEEDED_TO_CACHE的规定
    * 那么删除40%最近没有被使用的文件
    *
    *
    @param dirPath
    *
    @param filename
    */
    private boolean removeCache(String dirPath) {
    File dir = new File(dirPath);
    File[] files = dir.listFiles();
    if (files == null) {
    return true;
    }
    if (!android.os.Environment.getExternalStorageState().equals(
    android.os.Environment.MEDIA_MOUNTED)) {
    return false;
    }

    int dirSize = 0;
    for (int i = 0; i < files.length; i++) {
    if (files[i].getName().contains(WHOLESALE_CONV)) {
    dirSize += files[i].length();
    }
    }

    if (dirSize > CACHE_SIZE * MB
    || FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
    int removeFactor = (int) ((0.4 * files.length) + 1);

    Arrays.sort(files, new FileLastModifSort());

    Log.i("ImageFileCache", "清理缓存文件");
    for (int i = 0; i < removeFactor; i++) {

    if (files[i].getName().contains(WHOLESALE_CONV)) {
    files[i].delete();
    }

    }

    }

    if (freeSpaceOnSd() <= CACHE_SIZE) {
    return false;
    }
    return true;
    }

    /**
    * TODO 根据文件的最后修改时间进行排序 *
    */
    private class FileLastModifSort implements Comparator<File> {
    public int compare(File arg0, File arg1) {
    if (arg0.lastModified() > arg1.lastModified()) {
    return 1;
    } else if (arg0.lastModified() == arg1.lastModified()) {
    return 0;
    } else {
    return -1;
    }
    }
    }


    /**
    * 删除过期文件
    *
    *
    @param dirPath
    *
    @param filename
    */
    public void removeExpiredCache(String dirPath, String filename) {

    File file = new File(dirPath, filename);

    if (System.currentTimeMillis() - file.lastModified() > mTimeDiff) {

    Log.i("ImageFileCache", "Clear some expiredcache files ");

    file.delete();
    }

    }

    /**
    * 修改文件的最后修改时间
    * 这里需要考虑,是否将使用的图片日期改为当前日期
    *
    @param path
    */
    public void updateFileTime(String path) {
    File file = new File(path);
    long newModifiedTime = System.currentTimeMillis();
    file.setLastModified(newModifiedTime);
    }
    /**
    * 计算sdcard上的剩余空间
    *
    @return
    */
    private int MB=1024*1024;
    private int freeSpaceOnSd() {
    StatFs stat = new StatFs(Environment.getExternalStorageDirectory() .getPath());
    double sdFreeMB = ((double)stat.getAvailableBlocks() * (double) stat.getBlockSize()) / MB;
    return (int) sdFreeMB;
    }

    /**将url转成文件名**/
    private String convertUrlToFileName(String url) {
    String[] strs = url.split("/");
    return strs[strs.length - 1] + WHOLESALE_CONV;
    }
    /**获得缓存目录**/
    private String getDirectory() {
    String dir = getSDPath() + "/" + CACHDIR;
    String substr = dir.substring(0, 4);
    if (substr.equals("/mnt")) {
    dir = dir.replace("/mnt", "");
    }
    return dir;
    }
    /**** 取SD卡路径不带/ ****/
    public String getSDPath() {
    File sdDir = null;
    boolean sdCardExist = Environment.getExternalStorageState().equals(
    android.os.Environment.MEDIA_MOUNTED); // 判断sd卡是否存在
    if (sdCardExist) {
    sdDir = Environment.getExternalStorageDirectory();// 获取跟目录
    }
    if(sdDir!=null)
    {
    return sdDir.toString();
    }else
    {
    return "";
    }
    }
    }

    ImageGetForHttp.java

    package com.gowin.cach;

    import java.io.FilterInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import org.apache.http.HttpEntity;
    import org.apache.http.HttpResponse;
    import org.apache.http.HttpStatus;
    import org.apache.http.client.HttpClient;
    import org.apache.http.client.methods.HttpGet;

    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.net.http.AndroidHttpClient;
    import android.util.Log;

    public class ImageGetForHttp {
    private static final String LOG_TAG="ImageGetForHttp";
    public static Bitmap downloadBitmap(String url) {
    //final int IO_BUFFER_SIZE = 4 * 1024;

    // AndroidHttpClient is not allowed to be used from the main thread
    final HttpClient client = AndroidHttpClient.newInstance("Android");
    final HttpGet getRequest = new HttpGet(url);

    try {
    HttpResponse response = client.execute(getRequest);
    final int statusCode = response.getStatusLine().getStatusCode();
    if (statusCode != HttpStatus.SC_OK) {
    Log.w("ImageDownloader", "Error " + statusCode +
    " while retrieving bitmap from " + url);
    return null;
    }

    final HttpEntity entity = response.getEntity();
    if (entity != null) {
    InputStream inputStream = null;
    try {
    inputStream = entity.getContent();
    // return BitmapFactory.decodeStream(inputStream);
    // Bug on slow connections, fixed in future release.
    FilterInputStream fit= new FlushedInputStream(inputStream);
    return BitmapFactory.decodeStream(fit);
    } finally {
    if (inputStream != null) {
    inputStream.close();
    }
    entity.consumeContent();
    }
    }
    } catch (IOException e) {
    getRequest.abort();
    Log.w(LOG_TAG, "I/O error while retrieving bitmap from " + url, e);
    } catch (IllegalStateException e) {
    getRequest.abort();
    Log.w(LOG_TAG, "Incorrect URL: " + url);
    } catch (Exception e) {
    getRequest.abort();
    Log.w(LOG_TAG, "Error while retrieving bitmap from " + url, e);
    } finally {
    if ((client instanceof AndroidHttpClient)) {
    ((AndroidHttpClient) client).close();
    }
    }
    return null;
    }

    /*
    * An InputStream that skips the exact number of bytes provided, unless it reaches EOF.
    */
    static class FlushedInputStream extends FilterInputStream {
    public FlushedInputStream(InputStream inputStream) {
    super(inputStream);
    }

    @Override
    public long skip(long n) throws IOException {
    long totalBytesSkipped = 0L;
    while (totalBytesSkipped < n) {
    long bytesSkipped = in.skip(n - totalBytesSkipped);
    if (bytesSkipped == 0L) {
    int b = read();
    if (b < 0) {
    break; // we reached EOF
    } else {
    bytesSkipped = 1; // we read one byte
    }
    }
    totalBytesSkipped += bytesSkipped;
    }
    return totalBytesSkipped;
    }
    }


    }

    使用时,要救imageview的tag是需要下载的url

    在getview时先addtask

    当滚动停止的时候调用doTask






  • 相关阅读:
    Python使用struct处理二进制
    ProtoBuf与Python结合使用初步
    智能指针shared_ptr的用法
    C++模板
    mac 安装protobuf,并编译为java,c++,python
    java的HashCode方法
    JVM的内存结构
    HashSet与TreeSet的比较
    面向对象的特征有哪些方面?
    Collection与Collections的区别是什么?
  • 原文地址:https://www.cnblogs.com/meieiem/p/2223918.html
Copyright © 2011-2022 走看看