【问题标题】:Images not being cached locally (using Universal Image Loader) - slow image load times图像未在本地缓存(使用通用图像加载器) - 图像加载时间慢
【发布时间】:2012-07-11 13:00:57
【问题描述】:

问题描述: 我正在创建一个可滚动的文章列表,其中包含由我的 SQLite 数据库填充的缩略图。一般来说,它是“工作”的,除了速度很慢:

图像加载非常缓慢...我认为使用“Universal Image Loader”将图像缓存在设备上,如果您已经查看过它们(或至少接近)。但是 - 当你向上/向下拖动时,没有图像,然后 3-5 秒后,图像开始弹出(好像它正在重新下载它们)

我正在动态更改缩略图框的可见性,但效果很好 - 它们似乎没有改变 - 它们只是滚动到视图中,没有闪烁或任何东西。 (但随后图像不会再出现几秒钟)。

我已经通过在滚动后删除我的 php 脚本进行了测试...当我滚动回上一个位置时,图像不显示 - 让我假设它每次都从我的 PHP 脚本加载。

但根据the docs“UsingFreqLimitedMemoryCache(超过缓存大小限制时删除最不常用的位图)-默认使用”

详情:

在我的ArticleEntryAdapter.js 我有:

@Override
public View getView(final int position, final View convertView, final ViewGroup parent) {

    // We need to get the best view (re-used if possible) and then
    // retrieve its corresponding ViewHolder, which optimizes lookup efficiency
    final View view = getWorkingView(convertView);
    final ViewHolder viewHolder = getViewHolder(view);
    final Article article = getItem(position);

    // Set the title
    viewHolder.titleView.setText(article.title);

    //Set the subtitle (subhead) or description
    if(article.subtitle != null)
    {
        viewHolder.subTitleView.setText(article.subtitle);
    }
    else if(article.description != null)
    {
        viewHolder.subTitleView.setText(article.description);
    }

    ImageLoader imageLoader = ImageLoader.getInstance();

    imageLoader.displayImage("", viewHolder.thumbView); //clears previous one
    if(article.filepath != null && article.filepath.length() != 0) {
        imageLoader.displayImage(
            "http://img.sltdb.com/processes/resize.php?image=" + article.filepath + "&size=100&quality=70",
            viewHolder.thumbView
            );
        viewHolder.thumbView.setVisibility(View.VISIBLE);
    } else {
        viewHolder.thumbView.setVisibility(View.GONE);
    }

    return view;
}

至于图像不正确 - 这并不常见,但有时在滚动时,我会看到 2 个相同的图像,当我查看文章时,它们根本不相关(即没有机会实际上有相同的图像)所以 - 我滚动离开它,然后返回,它不再是不正确的图像。

注意:我是 Java/Android 新手 - 你可能已经注意到了。

每个评论请求的更多代码:

private View getWorkingView(final View convertView) {
    // The workingView is basically just the convertView re-used if possible
    // or inflated new if not possible
    View workingView = null;

    if(null == convertView) {
        final Context context = getContext();
        final LayoutInflater inflater = (LayoutInflater)context.getSystemService
          (Context.LAYOUT_INFLATER_SERVICE);

        workingView = inflater.inflate(articleItemLayoutResource, null);
    } else {
        workingView = convertView;
    }

    return workingView;
}

更新: 我的清单文件有:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

但是我找到的缓存文件夹完全是空的:

mnt
  -sdcard
    -Android
      -data
        -com.mysite.news
          -cache
            -uil-images

【问题讨论】:

  • 什么是getWorkingView(convertView);
  • @Sherif elKhatib - 已添加

标签: java android image listview


【解决方案1】:

我在列表视图中遇到了类似的图像问题。可能this answer 会纠正你错误的图像问题。

我刚刚使用 UniversalImageLoader 下载了示例项目,它表现出与您描述的相同的行为。

到目前为止查看源代码的一些注意事项。

public static final int DEFAULT_THREAD_POOL_SIZE = 3;
public static final int DEFAULT_THREAD_PRIORITY = Thread.NORM_PRIORITY - 1;
public static final int DEFAULT_MEMORY_CACHE_SIZE = 2 * 1024 * 1024; // bytes

这表示任何时候都会有 3 个线程下载和最多 2MB 的图像。你下载的图片有多大?你也缓存到磁盘吗?如果是这样,那会很慢。

要在 ImageLoader 中配置一些基本选项,您需要传入 displayImage:

 DisplayImageOptions options = new DisplayImageOptions.Builder()
     .showStubImage(R.drawable.stub_image)
     .cacheInMemory()
     .cacheOnDisc()
     .build();

我也希望您尝试以下选项:

ImageLoaderConfiguration imageLoaderConfiguration = new ImageLoaderConfiguration.Builder(this)
    .enableLogging()
    .memoryCacheSize(41943040)
    .discCacheSize(104857600)
    .threadPoolSize(10)
    .build();

imageLoader = ImageLoader.getInstance();
imageLoader.init(imageLoaderConfiguration);

通过我的测试,图像在磁盘上,但加载仍然很慢。

经过大量测试,我确定主要问题是 UniversalImageLoader 速度很慢。具体来说,ImageLoader 和 LoadAndDisplayImageTask 正在阻止工作。我(很快)将 LoadAndDisplayImageTask 重写为 AsyncTask,它立即表现得更好。您可以在 GitHub 上下载代码的分叉版本。

Universal Image Loader with AsyncTasks

【讨论】:

  • 图片不正确的事情并不经常发生——更大的问题是图片加载速度慢。如果它们被缓存,它们不应该只是看起来滑入而不是延迟几秒钟吗?虽然我很欣赏这个说明,但我会研究其他问题的答案。
  • 如果没有那行,该回收视图中的前一个图像会显示,直到新图像被加载。我把它拿出来,仍然有延迟。它似乎没有在设备上缓存任何内容 - 如果我加载应用程序,向下滚动,删除我的 PHP 代码,然后向上滚动,图像不会出现。
  • 图像没有被缓存在磁盘上(虽然我希望它们)。我会尝试你的建议,我很有希望,但根据文档,默认值应该将它们缓存到磁盘。我的图片很小 - 100x100px
  • ImageLoaderConfiguration 还允许您指定最大图像大小。我没有尝试该选项,但它可能会有所帮助。
  • 我想你在构建ImageLoaderConfiguration 时忘记打电话给.defaultDisplayImageOptions(displayImageOptions)。如果您不这样做,则根本不会应用那些 DisplayImageOptions 选项。我遇到了同样的缓慢,在应用 DisplayImageOptions 后消失了。
【解决方案2】:

另一种解决方案是来自点火开源项目的“RemoteImageView”。

http://kaeppler.github.com/ignition-docs/ignition-core/apidocs/com/github/ignition/core/widgets/RemoteImageView.html

实际上,RemoteImageView 扩展了 ImageView 并在幕后为您完成所有获取/缓存。

虽然它不一定能解决您列出的问题,但作为替代解决方案可能值得研究。

编辑:如果您仍需要远程图像解决方案,我强烈推荐 Picasso。我已将应用程序中的 RemoteImageView 替换为 Picasso:http://square.github.io/picasso/

【讨论】:

【解决方案3】:

我怀疑 resize.php 很慢,尤其是当它必须调整大页面的大小并且收到多个请求时。并且不知何故在 imageLoader 中缓存没有完成。

首先,我会在图像加载之后完成其余的工作:副标题、描述和所有内容。 因为如果图像加载时间过长,如果描述和其他所有内容一起出现,则会有更即时的效果。通常你的陈述顺序是好的。

@CameronLowellPallmer 的回答负责切换图像和缓存。

【讨论】:

  • resize.php 文件被缓存并在您在浏览器中点击 url 时立即加载(因为它们是 TINY 文件)。我不确定您所说的“图像加载后休息......”是什么意思(记住我是菜鸟 :)
  • 我将图片内容移到了标题之前...等内容 - 没有变化。
  • (更重要的是,图像应该被缓存在设备上 - 如果它每次都在访问我的 PHP 脚本,这本身就是一个问题!)
  • 我已经对我的回答进行了澄清。如果您对 PHP 有控制权,则可以验证 android 上的缓存行为,以及 resize 是否从 its 缓存中提供服务。
  • 要么是 a) 没有在本地缓存,要么 b) 没有使用本地缓存 - 当我删除我的 PHP 代码时(滚动一下之后),图像根本不再加载。
【解决方案4】:

这门课对我有用:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.params.HttpParams;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.ImageView;

public class ImageDownloader {

    Map<String,Bitmap> imageCache;

    public ImageDownloader(){
        imageCache = new HashMap<String, Bitmap>();

    }

    //download function
    public void download(String url, ImageView imageView) {
         if (cancelPotentialDownload(url, imageView)&&url!=null) {

             //Caching code right here
             String filename = String.valueOf(url.hashCode());
             File f = new File(getCacheDirectory(imageView.getContext()), filename);

              // Is the bitmap in our memory cache?
             Bitmap bitmap = null;

              bitmap = (Bitmap)imageCache.get(f.getPath());
                BitmapFactory.Options bfOptions=new BitmapFactory.Options();
                bfOptions.inDither=false;                     //Disable Dithering mode
                bfOptions.inPurgeable=true;                   //Tell to gc that whether it needs free memory, the Bitmap can be cleared
                bfOptions.inInputShareable=true;              //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future
                bfOptions.inTempStorage=new byte[32 * 1024]; 
                FileInputStream fs=null;

              if(bitmap == null){

                  //bitmap = BitmapFactory.decodeFile(f.getPath(),options);
                  try {
                      fs = new FileInputStream(f);
                        if(fs!=null) bitmap=BitmapFactory.decodeFileDescriptor(fs.getFD(), null, bfOptions);
                    } catch (IOException e) {
                        //TODO do something intelligent
                        e.printStackTrace();
                    } finally{ 
                        if(fs!=null) {
                            try {
                                fs.close();
                            } catch (IOException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                        }
                    }

                  if(bitmap != null){
                      imageCache.put(f.getPath(), bitmap);
                  }

              }
              //No? download it
              if(bitmap == null){
                  BitmapDownloaderTask task = new BitmapDownloaderTask(imageView);
                  DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task);
                  imageView.setImageDrawable(downloadedDrawable);
                  task.execute(url);
              }else{
                  //Yes? set the image
                  imageView.setImageBitmap(bitmap);
              }
         }
    }

    //cancel a download (internal only)
    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;
    }

    //gets an existing download if one exists for the imageview
    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;
    }

    //our caching functions
    // Find the dir to save cached images
    public static File getCacheDirectory(Context context){
        String sdState = android.os.Environment.getExternalStorageState();
        File cacheDir;

        if (sdState.equals(android.os.Environment.MEDIA_MOUNTED)) {
            File sdDir = android.os.Environment.getExternalStorageDirectory();  

            //TODO : Change your diretcory here
            cacheDir = new File(sdDir,"data/tac/images");
        }
        else
            cacheDir = context.getCacheDir();

        if(!cacheDir.exists())
            cacheDir.mkdirs();
            return cacheDir;
    }

    private void writeFile(Bitmap bmp, File f) {
          FileOutputStream out = null;

          try {
            out = new FileOutputStream(f);
            bmp.compress(Bitmap.CompressFormat.PNG, 80, out);
          } catch (Exception e) {
            e.printStackTrace();
          }
          finally { 
            try { if (out != null ) out.close(); }
            catch(Exception ex) {} 
          }
    }
    ///////////////////////

    //download asynctask
    public class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
        private String url;
        private final WeakReference<ImageView> imageViewReference;

        public BitmapDownloaderTask(ImageView imageView) {
            imageViewReference = new WeakReference<ImageView>(imageView);
        }

        @Override
        // Actual download method, run in the task thread
        protected Bitmap doInBackground(String... params) {
             // params comes from the execute() call: params[0] is the url.
             url = (String)params[0];
             return downloadBitmap(params[0]);
        }

        @Override
        // Once the image is downloaded, associates it to the imageView
        protected void onPostExecute(Bitmap bitmap) {
            if (isCancelled()) {
                bitmap = null;
            }

            if (imageViewReference != null) {
                ImageView imageView = imageViewReference.get();
                BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
                // Change bitmap only if this process is still associated with it
                if (this == bitmapDownloaderTask) {
                    imageView.setImageBitmap(bitmap);

                    //cache the image


                    String filename = String.valueOf(url.hashCode());
                    File f = new File(getCacheDirectory(imageView.getContext()), filename);

                    imageCache.put(f.getPath(), bitmap);

                    writeFile(bitmap, f);
                }
            }
        }


    }

    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();
        }
    }

    //the actual download code
    static Bitmap downloadBitmap(String url) {
        HttpParams params = new BasicHttpParams();
        params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
        HttpClient client = new DefaultHttpClient(params);
        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(); 
                    final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                    return bitmap;
                } finally {
                    if (inputStream != null) {
                        inputStream.close();  
                    }
                    entity.consumeContent();
                }
            }
        } catch (Exception e) {
            // Could provide a more explicit error message for IOException or IllegalStateException
            getRequest.abort();
            Log.w("ImageDownloader", "Error while retrieving bitmap from " + url + e.toString());
        } finally {
            if (client != null) {
                //client.close();
            }
        }
        return null;
    }
}

使用示例:

downloader = new ImageDownloader();
ImageView image_profile =(ImageView) row.findViewById(R.id.image_profile);
downloader.download(url, image_profile);

【讨论】:

    猜你喜欢
    • 2012-08-19
    • 1970-01-01
    • 2013-05-02
    • 2020-03-31
    • 2016-02-29
    • 2014-01-19
    • 1970-01-01
    • 1970-01-01
    • 2014-11-27
    相关资源
    最近更新 更多