2012-07-11 49 views
9

问题描述: 我创建的文章与同时通过我的SQLite数据库填充缩略图的滚动列表。一般来说,它是“工作”,除了速度慢:图片不被本地缓存(采用通用图像装载机) - 慢图像加载时间

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

我改变缩略图的可见性即时通讯盒,但它的工作完美无瑕 - 它们看起来并没有改变 - 它们只是滚动查看或不查看,不闪烁或任何东西。 (但之后图像不会再出现几秒钟)。

我已经通过在滚动后删除我的PHP脚本进行了测试...当我滚动回prev spot时,图像不显示 - 使我假设它每次都从我的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; 
} 

UPDATE: 我的清单文件有:

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

不过我发现缓存文件夹完全是空的:

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

是什么'getWorkingView(convertView);'? – 2012-07-17 05:54:22

+0

@Sherif elKhatib - 增加了 – Dave 2012-07-17 12:53:23

回答

19

我在列表视图中遇到类似的图像问题。可能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 

这就说明在任何时候都会有三个线程下载和最大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

+0

不正确的图像事情不经常发生 - 更大的问题是图像加载速度缓慢。如果他们被缓存了,他们是不是应该看起来滑了一下,而不是被延迟多秒?虽然我很欣赏这个说明,并会考虑其他问题的答案。 – Dave 2012-07-16 14:24:14

+0

如果没有这一行,那个回收视图中的前一个图像就会显示出来,直到新图像加载完毕。我将它取出,并且仍然存在延迟。它似乎没有缓存设备上的任何东西 - 如果我加载应用程序,向下滚动,删除我的PHP代码,然后向上滚动,图像不会出现。 – Dave 2012-07-16 15:13:38

+0

图像没有被缓存在磁盘上(尽管我喜欢它们)。我会尝试你的建议,而且我很有希望,但根据文档,DEFAULT应该将它们缓存到磁盘。我的图像很小 - 100x100px – Dave 2012-07-17 12:47:36

1

我怀疑resize.php是缓慢的,尤其是如果它有调整大页面,并接收到几个请求。不知何故,在imageLoader缓存没有完成。

首先我会做之后的其他图片加载:字幕,说明等全部。 因为如果图片加载时间过长,如果描述和所有其他图片一起出现,则会产生更多瞬间效果。通常你的发言顺序很好。

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

+0

resize.php文件被缓存,并在浏览器中点击url时立即加载(因为它们是TINY文件)。我不确定你的意思是“在图像加载后休息......”(记住我是一个noob :) – Dave 2012-07-16 14:20:39

+0

我在标题之前移动图像的东西...等东西 - 没有变化。 – Dave 2012-07-16 14:29:28

+0

(更重要的是,图像应该被缓存在设备上 - 如果它每次都打到我的PHP脚本,这本身就是一个问题!) – Dave 2012-07-16 14:32:34

3

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

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

实际上,RemoteImageView延伸ImageView的和做所有的读取/缓存为你在幕后。

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

编辑:如果您仍然需要远程图像解决方案,我会强烈推荐毕加索。我把它换成RemoteImageView在我的应用程序与毕加索:http://square.github.io/picasso/

+0

是否有任何指导/教程网站? – Dave 2012-07-16 20:55:17

+0

点火项目中的活动示例在这里有一个非常简单的用法:https://github.com/kaeppler/ignition/blob/master/ignition-core/ignition-core-samples/src/com/github/ignition/samples /core/RemoteImageViewActivity.java – theelfismike 2012-07-16 22:17:55

0

此类工作对我来说:使用

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