Android图像下载器/缓存管理器

Android图像下载器/缓存管理器,android,caching,Android,Caching,马斯特斯,我正在用android开发一个类似Google plus的应用程序。它将呈现来自服务器的提要。我正在使用自定义listview并填充它。在这两者之间,我在每个列表项中都有要显示的图像 我正在使用下面的代码下载图像并在视图中显示 我从.,和许多其他地方得到了它,我看到了使用相同的代码 这里的问题是 缓存没有发生。我记录了这些电话,发现每次图像都是新下载的 我需要一个解决方案,我需要存储一些图像,当请求url出现时,它应该检查现有的缓存并提供图像。不需要再次从网上下载 有谁能给我推荐一些好

马斯特斯,我正在用android开发一个类似Google plus的应用程序。它将呈现来自服务器的提要。我正在使用自定义listview并填充它。在这两者之间,我在每个列表项中都有要显示的图像

我正在使用下面的代码下载图像并在视图中显示

我从.,和许多其他地方得到了它,我看到了使用相同的代码

这里的问题是

  • 缓存没有发生。我记录了这些电话,发现每次图像都是新下载的
  • 我需要一个解决方案,我需要存储一些图像,当请求url出现时,它应该检查现有的缓存并提供图像。不需要再次从网上下载
  • 有谁能给我推荐一些好的库来做同样的事情,或者简单地修复下面的代码。

    请注意,我不是专家:)

    /*
    *版权所有(C)2010安卓开源项目
    *
    *根据Apache许可证2.0版(以下简称“许可证”)获得许可;
    *除非遵守许可证,否则不得使用此文件。
    *您可以通过以下方式获得许可证副本:
    *
    *      http://www.apache.org/licenses/LICENSE-2.0
    *
    *除非适用法律要求或书面同意,软件
    *根据许可证进行的分发是按“原样”进行分发的,
    *无任何明示或暗示的保证或条件。
    *请参阅许可证以了解管理权限和权限的特定语言
    *许可证下的限制。
    */
    /**
    *这个助手类从Internet下载图像,并将这些图像与提供的ImageView绑定。
    *
    *它需要INTERNET权限,该权限应添加到应用程序的清单中
    *文件

    * *下载图像的本地缓存在内部维护,以提高性能。 */ 公共类图像下载程序{ 私有静态最终字符串日志\u TAG=“ImageDownloader”; 公共枚举模式{NO_ASYNC_TASK,NO_download_DRAWABLE,CORRECT} 私有模式=模式。无异步任务; /** *从Internet下载指定的图像并将其绑定到提供的ImageView *如果在缓存中找到映像,则绑定是立即的,并且将异步完成 *否则。如果发生错误,则会将空位图关联到ImageView。 * *@param url要下载的图像的url。 *@param imageView将下载的图像绑定到的imageView。 */ 公共void下载(字符串url,ImageView){ 重置PurgeTimer(); 位图位图=getBitmapFromCache(url); 如果(位图==null){ 强制下载(url、imageView); }否则{ 取消潜在下载(url、imageView); 设置图像位图(位图); } } /* *与下载相同,但始终下载映像,不使用缓存。 *由于利益不明,目前保密。 私有void forceDownload(字符串url、图像视图){ 强制下载(url、视图、空); } */ /** *与下载相同,但始终下载映像,不使用缓存。 *由于利益不明,目前保密。 */ 私有void forceDownload(字符串url,ImageView){ //状态健全性:url在下载的可绘制和缓存密钥中保证永远不为空。 如果(url==null){ imageView.setImageDrawable(空); 返回; } if(取消潜在下载(url,图像视图)){ 开关(模式){ 案例编号异步任务: 位图位图=下载位图(url); addBitmapToCache(url、位图); 设置图像位图(位图); 打破 案例编号\u下载\u可提取: imageView.setMinimumHeight(156); BitmapDownloaderTask任务=新建BitmapDownloaderTask(图像视图); task.execute(url); 打破 大小写正确: 任务=新位图下载任务(imageView); DownloadedDrawable DownloadedDrawable=新的DownloadedDrawable(任务); imageView.setImageDrawable(可下载Drawable); imageView.setMinimumHeight(156); task.execute(url); 打破 } } } /** *如果当前下载已取消或中没有下载,则返回true *此图像视图的进度。 *如果正在进行的下载处理相同的url,则返回false。下载不正确 *在那种情况下停止。 */ 私有静态布尔cancelPotentialDownload(字符串url,ImageView ImageView){ BitmapDownloaderTask BitmapDownloaderTask=getBitmapDownloaderTask(图像视图); if(bitmapDownloaderTask!=null){ 字符串bitmapUrl=bitmapDownloaderTask.url; 如果((bitmapUrl==null)| |(!bitmapUrl.equals(url))){ bitmapDownloaderTask.cancel(true); }否则{ //已下载相同的URL。 返回false; } } 返回true; } /** *@param imageView任意imageView *@return检索与此imageView关联的当前活动下载任务(如果有)。 *如果没有此类任务,则为null。 */ 私有静态位图下载任务getBitmapDownloaderTask(ImageView ImageView){ 如果(imageView!=null){ Drawable Drawable=imageView.getDrawable(); if(可下载的可绘制实例可绘制){ DownloadedDrawable DownloadedDrawable=(DownloadedDrawable)可绘制; 返回downloadedDrawable.getBitmapDownloaderTask(); } } 返回null; } 位图下载位图(字符串url)
    /*
     * Copyright (C) 2010 The Android Open Source Project
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    
    /**
     * This helper class download images from the Internet and binds those with the provided ImageView.
     *
     * <p>It requires the INTERNET permission, which should be added to your application's manifest
     * file.</p>
     *
     * A local cache of downloaded images is maintained internally to improve performance.
     */
    public class ImageDownloader {
        private static final String LOG_TAG = "ImageDownloader";
    
        public enum Mode { NO_ASYNC_TASK, NO_DOWNLOADED_DRAWABLE, CORRECT }
        private Mode mode = Mode.NO_ASYNC_TASK;
    
        /**
         * Download the specified image from the Internet and binds it to the provided ImageView. The
         * binding is immediate if the image is found in the cache and will be done asynchronously
         * otherwise. A null bitmap will be associated to the ImageView if an error occurs.
         *
         * @param url The URL of the image to download.
         * @param imageView The ImageView to bind the downloaded image to.
         */
        public void download(String url, ImageView imageView) {
            resetPurgeTimer();
            Bitmap bitmap = getBitmapFromCache(url);
    
            if (bitmap == null) {
                forceDownload(url, imageView);
            } else {
                cancelPotentialDownload(url, imageView);
                imageView.setImageBitmap(bitmap);
            }
        }
    
        /*
         * Same as download but the image is always downloaded and the cache is not used.
         * Kept private at the moment as its interest is not clear.
           private void forceDownload(String url, ImageView view) {
              forceDownload(url, view, null);
           }
         */
    
        /**
         * Same as download but the image is always downloaded and the cache is not used.
         * Kept private at the moment as its interest is not clear.
         */
        private void forceDownload(String url, ImageView imageView) {
            // State sanity: url is guaranteed to never be null in DownloadedDrawable and cache keys.
            if (url == null) {
                imageView.setImageDrawable(null);
                return;
            }
    
            if (cancelPotentialDownload(url, imageView)) {
                switch (mode) {
                    case NO_ASYNC_TASK:
                        Bitmap bitmap = downloadBitmap(url);
                        addBitmapToCache(url, bitmap);
                        imageView.setImageBitmap(bitmap);
                        break;
    
                    case NO_DOWNLOADED_DRAWABLE:
                        imageView.setMinimumHeight(156);
                        BitmapDownloaderTask task = new BitmapDownloaderTask(imageView);
                        task.execute(url);
                        break;
    
                    case CORRECT:
                        task = new BitmapDownloaderTask(imageView);
                        DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task);
                        imageView.setImageDrawable(downloadedDrawable);
                        imageView.setMinimumHeight(156);
                        task.execute(url);
                        break;
                }
            }
        }
    
        /**
         * Returns true if the current download has been canceled or if there was no download in
         * progress on this image view.
         * Returns false if the download in progress deals with the same url. The download is not
         * stopped in that case.
         */
        private static boolean cancelPotentialDownload(String url, ImageView imageView) {
            BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
    
            if (bitmapDownloaderTask != null) {
                String bitmapUrl = bitmapDownloaderTask.url;
                if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {
                    bitmapDownloaderTask.cancel(true);
                } else {
                    // The same URL is already being downloaded.
                    return false;
                }
            }
            return true;
        }
    
        /**
         * @param imageView Any imageView
         * @return Retrieve the currently active download task (if any) associated with this imageView.
         * null if there is no such task.
         */
        private static BitmapDownloaderTask getBitmapDownloaderTask(ImageView imageView) {
            if (imageView != null) {
                Drawable drawable = imageView.getDrawable();
                if (drawable instanceof DownloadedDrawable) {
                    DownloadedDrawable downloadedDrawable = (DownloadedDrawable)drawable;
                    return downloadedDrawable.getBitmapDownloaderTask();
                }
            }
            return null;
        }
    
        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 = (mode == Mode.NO_ASYNC_TASK) ? new DefaultHttpClient() :
                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(new FlushedInputStream(inputStream));
                    } 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;
        }
    
        /**
         * A patched InputSteam that tries harder to fully read the input stream.
         */
        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) break;
                    totalBytesSkipped += bytesSkipped;
                }
                return totalBytesSkipped;
            }
        }
    
        /**
         * The actual AsyncTask that will asynchronously download the image.
         */
        class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
            private String url;
            private final WeakReference<ImageView> imageViewReference;
    
            public BitmapDownloaderTask(ImageView imageView) {
                imageViewReference = new WeakReference<ImageView>(imageView);
            }
    
            /**
             * Actual download method.
             */
            @Override
            protected Bitmap doInBackground(String... params) {
                url = params[0];
                return downloadBitmap(url);
            }
    
            /**
             * Once the image is downloaded, associates it to the imageView
             */
            @Override
            protected void onPostExecute(Bitmap bitmap) {
                if (isCancelled()) {
                    bitmap = null;
                }
    
                addBitmapToCache(url, bitmap);
    
                if (imageViewReference != null) {
                    ImageView imageView = imageViewReference.get();
                    BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
                    // Change bitmap only if this process is still associated with it
                    // Or if we don't use any bitmap to task association (NO_DOWNLOADED_DRAWABLE mode)
                    if ((this == bitmapDownloaderTask) || (mode != Mode.CORRECT)) {
                        imageView.setImageBitmap(bitmap);
                    }
                }
            }
        }
    
    
        /**
         * A fake Drawable that will be attached to the imageView while the download is in progress.
         *
         * <p>Contains a reference to the actual download task, so that a download task can be stopped
         * if a new binding is required, and makes sure that only the last started download process can
         * bind its result, independently of the download finish order.</p>
         */
        static class DownloadedDrawable extends ColorDrawable {
            private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference;
    
            public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) {
                super(Color.BLACK);
                bitmapDownloaderTaskReference =
                    new WeakReference<BitmapDownloaderTask>(bitmapDownloaderTask);
            }
    
            public BitmapDownloaderTask getBitmapDownloaderTask() {
                return bitmapDownloaderTaskReference.get();
            }
        }
    
        public void setMode(Mode mode) {
            this.mode = mode;
            clearCache();
        }
    
    
        /*
         * Cache-related fields and methods.
         * 
         * We use a hard and a soft cache. A soft reference cache is too aggressively cleared by the
         * Garbage Collector.
         */
    
        private static final int HARD_CACHE_CAPACITY = 10;
        private static final int DELAY_BEFORE_PURGE = 10 * 1000; // in milliseconds
    
        // Hard cache, with a fixed maximum capacity and a life duration
        private final HashMap<String, Bitmap> sHardBitmapCache =
            new LinkedHashMap<String, Bitmap>(HARD_CACHE_CAPACITY / 2, 0.75f, true) {
            @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
                    sSoftBitmapCache.put(eldest.getKey(), new SoftReference<Bitmap>(eldest.getValue()));
                    return true;
                } else
                    return false;
            }
        };
    
        // Soft cache for bitmaps kicked out of hard cache
        private final static ConcurrentHashMap<String, SoftReference<Bitmap>> sSoftBitmapCache =
            new ConcurrentHashMap<String, SoftReference<Bitmap>>(HARD_CACHE_CAPACITY / 2);
    
        private final Handler purgeHandler = new Handler();
    
        private final Runnable purger = new Runnable() {
            public void run() {
                clearCache();
            }
        };
    
        /**
         * Adds this bitmap to the cache.
         * @param bitmap The newly downloaded bitmap.
         */
        private void addBitmapToCache(String url, Bitmap bitmap) {
            if (bitmap != null) {
                synchronized (sHardBitmapCache) {
                    sHardBitmapCache.put(url, bitmap);
                }
            }
        }
    
        /**
         * @param url The URL of the image that will be retrieved from the cache.
         * @return The cached bitmap or null if it was not found.
         */
        private Bitmap getBitmapFromCache(String url) {
            // First try the hard reference cache
            synchronized (sHardBitmapCache) {
                final Bitmap bitmap = sHardBitmapCache.get(url);
                if (bitmap != null) {
                    // Bitmap found in hard cache
                    // Move element to first position, so that it is removed last
                    sHardBitmapCache.remove(url);
                    sHardBitmapCache.put(url, bitmap);
                    return bitmap;
                }
            }
    
            // Then try the soft reference cache
            SoftReference<Bitmap> bitmapReference = sSoftBitmapCache.get(url);
            if (bitmapReference != null) {
                final Bitmap bitmap = bitmapReference.get();
                if (bitmap != null) {
                    // Bitmap found in soft cache
                    return bitmap;
                } else {
                    // Soft reference has been Garbage Collected
                    sSoftBitmapCache.remove(url);
                }
            }
    
            return null;
        }
    
        /**
         * Clears the image cache used internally to improve performance. Note that for memory
         * efficiency reasons, the cache will automatically be cleared after a certain inactivity delay.
         */
        public void clearCache() {
            sHardBitmapCache.clear();
            sSoftBitmapCache.clear();
        }
    
        /**
         * Allow a new delay before the automatic cache clear is done.
         */
        private void resetPurgeTimer() {
            purgeHandler.removeCallbacks(purger);
            purgeHandler.postDelayed(purger, DELAY_BEFORE_PURGE);
        }
    }
    
    public class ImageLoader {
    
    private MemoryCache memoryCache = new MemoryCache();
    private FileCache fileCache = null;
    private Map<ImageView, String> imageViews = Collections
            .synchronizedMap(new WeakHashMap<ImageView, String>());
    ExecutorService executorService;
    
    private static ImageLoader instance = null;
    
    private ImageLoader() {
    
    }
    
    private ImageLoader(Context context) {
    
        fileCache = new FileCache(context);
    
        executorService = Executors.newFixedThreadPool(5);
    }
    
    public static ImageLoader getInstance(Context context) {
        if (instance == null)
            instance = new ImageLoader(context);
        return instance;
    }
    
    public static void setInstance(ImageLoader instance) {
        ImageLoader.instance = instance;
    }
    
    final int stub_id = R.drawable.no_img;
    
    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));
    }
    
    public Bitmap getBitmap(String url) {
        File f = fileCache.getFile(url);
    
        // from SD cache
        Bitmap b = decodeFile(f);
        if (b != null)
            return b;
    
        // from web
        try {
            Bitmap bitmap = null;
            URL imageUrl = new URL(url);
            HttpURLConnection conn = (HttpURLConnection) imageUrl
                    .openConnection();
            conn.setConnectTimeout(30000);
            conn.setReadTimeout(30000);
            conn.setInstanceFollowRedirects(true);
            InputStream is = new BufferedInputStream(conn.getInputStream());
            OutputStream os = new FileOutputStream(f);
            Utils.CopyStream(is, os);
            os.close();
            bitmap = decodeFile(f);
            return bitmap;
        } catch (Exception ex) {
            return null;
        }
    }
    
    // decodes image and scales it to reduce memory consumption
    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
    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;
        }
    
        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);
            Activity a = (Activity) photoToLoad.imageView.getContext();
            a.runOnUiThread(bd);
        }
    }
    
    boolean imageViewReused(PhotoToLoad photoToLoad) {
        String tag = imageViews.get(photoToLoad.imageView);
        if (tag == null || !tag.equals(photoToLoad.url))
            return true;
        return false;
    }
    
    // Used to display bitmap in the UI thread
    class BitmapDisplayer implements Runnable {
        Bitmap bitmap;
        PhotoToLoad photoToLoad;
    
        public BitmapDisplayer(Bitmap b, PhotoToLoad p) {
            bitmap = b;
            photoToLoad = p;
        }
    
        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 class MemoryCache {
    private Map<String, SoftReference<Bitmap>> cache=Collections.synchronizedMap(new HashMap<String, SoftReference<Bitmap>>());
    
    public Bitmap get(String id){
        if(!cache.containsKey(id))
            return null;
        SoftReference<Bitmap> ref=cache.get(id);
        return ref.get();
    }
    
    public void put(String id, Bitmap bitmap){
        cache.put(id, new SoftReference<Bitmap>(bitmap));
    }
    
    public void clear() {
        cache.clear();
    }}
    
    public class Utils {
    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 ex){}
    }}
    
     private ImageLoader imageLoader = null;
    imageLoader = ImageLoader.getInstance(context);
    ImageView image = (ImageView) findViewById(R.id.img);
    imageLoader.DisplayImage(details.url_1, image);