下载图像时防止应用程序挂起?
Posted
技术标签:
【中文标题】下载图像时防止应用程序挂起?【英文标题】:Preventing application from hanging while image is downloading? 【发布时间】:2012-05-25 20:24:35 【问题描述】:我正在使用 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;
【问题讨论】:
看到这个***.com/questions/541966/… 请发布代码。我还发布了如何执行下载并添加到列表视图中? 位图 bmImg;位图 downloadFile(String fileUrl) URL myFileUrl = null;尝试 myFileUrl = 新 URL(fileUrl); catch (MalformedURLException e) e.printStackTrace(); 尝试 HttpURLConnection conn = (HttpURLConnection) myFileUrl .openConnection(); conn.setDoInput(true);连接.connect(); InputStream = conn.getInputStream(); bmImg = BitmapFactory.decodeStream(is); catch (IOException e) e.printStackTrace(); 返回 bmImg; 在网上搜索时,我发现了一些有趣的链接来解决您的问题 - github.com/thest1/LazyList - codehenge.net/blog/2011/06/… 希望这些对您有所帮助 与 android Lazy List with Images 一起去 【参考方案1】: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();
【讨论】:
【参考方案2】:我在这里读到你的问题是尝试这个的代码......
公共类 ImageDownloader private static final String LOG_TAG = "ImageDownloader";
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);
【讨论】:
以上是关于下载图像时防止应用程序挂起?的主要内容,如果未能解决你的问题,请参考以下文章
iOS AVPlayer 后台播放问题自动停止问题 防止应用被后台挂起方法