在下载图像时防止应用程序挂起?

时间:2012-05-18 08:58:37

标签: android performance process download

我正在使用ListView来显示与这些图像相关的一些图像和标题。我从互联网上获取图像。有没有办法延迟加载图像,以便在文本显示时,UI不会被锁定,图像会在下载时显示。图像数量不固定。

更新

(使用下面的代码,我试图下载图像并在ListView中显示)

Bitmap bmImg; Bitmap downloadFile(String fileUrl) 
{ 
 URL myFileUrl = null; 
  try { 
      myFileUrl = new URL(fileUrl); 
   } 
   catch (MalformedURLException e) 
   { 
      e.printStackTrace(); 
   } 

   try { 
     HttpURLConnection conn = (HttpURLConnection) myFileUrl .openConnection(); 
    conn.setDoInput(true); 
    conn.connect(); 
    InputStream is = conn.getInputStream(); 
    bmImg = BitmapFactory.decodeStream(is); 
   } catch (IOException e) 
    { 
        e.printStackTrace(); 
   } 
    return bmImg; 
  } 

2 个答案:

答案 0 :(得分:0)

import java.io.IOException;

public class DrawableManager {
    private final Map<String, Drawable> drawableMap;

    public DrawableManager() {
        drawableMap = new HashMap<String, Drawable>();
    }

    public Drawable fetchDrawable(String urlString) {
        if (drawableMap.containsKey(urlString)) {
            return drawableMap.get(urlString);
        }

        Log.d(this.getClass().getSimpleName(), "image url:" + urlString);
        try {
            InputStream is = fetch(urlString);
            Drawable drawable = Drawable.createFromStream(is, "src");


            if (drawable != null) {
                drawableMap.put(urlString, drawable);
                Log.d(this.getClass().getSimpleName(), "got a thumbnail drawable: " + drawable.getBounds() + ", "
                        + drawable.getIntrinsicHeight() + "," + drawable.getIntrinsicWidth() + ", "
                        + drawable.getMinimumHeight() + "," + drawable.getMinimumWidth());
            } else {
              Log.w(this.getClass().getSimpleName(), "could not get thumbnail");
            }

            return drawable;
        } catch (MalformedURLException e) {
            Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
            return null;
        } catch (IOException e) {
            Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
            return null;
        }
    }

    public void fetchDrawableOnThread(final String urlString, final ImageView imageView) {
        if (drawableMap.containsKey(urlString)) {
            imageView.setImageDrawable(drawableMap.get(urlString));
        }

        final Handler handler = new Handler() {
            @Override
            public void handleMessage(Message message) {
                imageView.setImageDrawable((Drawable) message.obj);
            }
        };

        Thread thread = new Thread() {
            @Override
            public void run() {
                //TODO : set imageView to a "pending" image
                Drawable drawable = fetchDrawable(urlString);
                Message message = handler.obtainMessage(1, drawable);
                handler.sendMessage(message);
            }
        };
        thread.start();
    }

    private InputStream fetch(String urlString) throws MalformedURLException, IOException {
        DefaultHttpClient httpClient = new DefaultHttpClient();
        HttpGet request = new HttpGet(urlString);
        HttpResponse response = httpClient.execute(request);
        return response.getEntity().getContent();
    }

}

答案 1 :(得分:0)

我在这里看到你的问题是尝试这个的代码...

public class ImageDownloader {     private static final String LOG_TAG =&#34; ImageDownloader&#34 ;;

public enum Mode {
    NO_ASYNC_TASK, NO_DOWNLOADED_DRAWABLE, CORRECT
}

private Mode mode = Mode.CORRECT;

/**
 * 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) {

    if (url != null && !url.isEmpty()) {
        url = url.replaceAll(" ", "%20");
        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(inputStream);
                // Bug on slow connections, fixed in future release.
                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;
}

/*
 * 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;
    }
}

/**
 * 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.WHITE);
        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 = 1000;
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();
        System.gc();
    }
};

/**
 * 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);
}

}