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

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 后台播放问题自动停止问题 防止应用被后台挂起方法

NSURLSessionDownloadTask 在挂起时继续下载

如何防止 Eclipse 在启动时挂起?

打开故事板时如何防止 XCode5 挂起/沙滩球?

IOS如何防止下载的图片以编程方式缓存