2011-11-03 35 views
7

第一问题:Android - 线程池策略和可以用Loader来实现它吗?

  • 我正在使用自定义FragmentStatePagerAdapter内的多个FragmentLists 应用。可能有, 潜在大量的这样的片段说20至40之间。
  • 每个片段是一个列表,其中每个项目可能包含文本或图像。
  • 的图像需要从网络异步上传并缓存到临时内存缓存,并为SD(如果可用)
  • 当片段熄灭屏幕任意上传和当前活动应取消(不暂停)

我的第一个实现遵循Google公司的image loader code。我的代码问题是,它基本上为每个图像创建一个AsyncTask的实例。在我的情况下,杀死应用程序真的很快。

由于我使用的是v4兼容包,我认为使用定制的Loader扩展AsyncTaskLoader会帮助我,因为它在内部实现了一个线程池。但是,如果我多次执行此代码,则每次后续调用都会中断以前的操作,令我感到不愉快的惊喜。说我有这在我的ListView#getView方法:

getSupportLoaderManager().restartLoader(0, args, listener); 

这种方法是在循环对于映入眼帘的每个列表项目执行。正如我所说 - 每个以后的调用将终止前一个。或者至少这是发生什么基础上的logcat

11-03 13:33:34.910: V/LoaderManager(14313): restartLoader in LoaderManager: args=Bundle[{URL=http://blah-blah/pm.png}] 
11-03 13:33:34.920: V/LoaderManager(14313): Removing pending loader: LoaderInfo{405d44c0 #2147483647 : ImageLoader{405118a8}} 
11-03 13:33:34.920: V/LoaderManager(14313): Destroying: LoaderInfo{405d44c0 #2147483647 : ImageLoader{405118a8}} 
11-03 13:33:34.920: V/LoaderManager(14313): Enqueuing as new pending loader 

然后我想,也许给唯一的ID给每个装载机将有助于问题,但它似乎并没有任何区别。结果,我看起来随机的图像和应用程序永远不会加载我所需要的四分之一。

问题

  • 什么是修复装载机做我想做什么(有没有办法?)
  • 的方式,如果没有什么是创造AsyncTask池的好办法,是那里可能正在执行它?

为了让您知道这里的代码是精简版的Loader,其中实际的下载/保存逻辑位于单独的ImageManager类中。

public class ImageLoader extends AsyncTaskLoader<TaggedDrawable> { 
     private static final String TAG = ImageLoader.class.getName(); 
     /** Wrapper around BitmapDrawable that adds String field to id the drawable */ 
     TaggedDrawable img; 
     private final String url; 
     private final File cacheDir; 
     private final HttpClient client; 


    /** 
    * @param context 
    */ 
    public ImageLoader(final Context context, final String url, final File cacheDir, final HttpClient client) { 
     super(context); 
     this.url = url; 
     this.cacheDir = cacheDir; 
     this.client = client; 
    } 

    @Override 
    public TaggedDrawable loadInBackground() { 
     Bitmap b = null; 
     // first attempt to load file from SD 
     final File f = new File(this.cacheDir, ImageManager.getNameFromUrl(url)); 
     if (f.exists()) { 
      b = BitmapFactory.decodeFile(f.getPath()); 
     } else { 
      b = ImageManager.downloadBitmap(url, client); 
      if (b != null) { 
       ImageManager.saveToSD(url, cacheDir, b); 
      } 
     } 
     return new TaggedDrawable(url, b); 
    } 

    @Override 
    protected void onStartLoading() { 
     if (this.img != null) { 
      // If we currently have a result available, deliver it immediately. 
      deliverResult(this.img); 
     } else { 
      forceLoad(); 
     } 
    } 

    @Override 
    public void deliverResult(final TaggedDrawable img) { 
     this.img = img; 
     if (isStarted()) { 
      // If the Loader is currently started, we can immediately deliver its results. 
      super.deliverResult(img); 
     } 
    } 

    @Override 
    protected void onStopLoading() { 
     // Attempt to cancel the current load task if possible. 
     cancelLoad(); 
    } 

    @Override 
    protected void onReset() { 
     super.onReset(); 
     // Ensure the loader is stopped 
     onStopLoading(); 
     // At this point we can release the resources associated with 'apps' 
     // if needed. 
     if (this.img != null) { 
      this.img = null; 
     } 

    } 

} 
+0

'AsyncTask'已经使用了一个池。该池可以达到128个线程IIRC,这可能是您难度的来源。你可以使用'java.util.concurrent'类来实现你自己的线程池。 – CommonsWare

+1

如果您的开发针对Android 3.0(API Level 11),则可以使用新添加的API [AsyncTask.executeOnExecutor()](http://developer.android.com/reference/android/os/AsyncTask.html#executeOnExecutor% 28java.util.concurrent.Executor,%20Params ...%29)可以很好地控制你的AsyncTask创建生命周期的线程池。 – yorkw

+0

AsynkTask不过只能执行一次,所以我需要为每个加载的图像创建一个实例。使它60,这是很多的对象 – Bostone

回答

11

好吧,首先要做的第一件事。 Android附带的AsyncTask不应该淹没你的应用程序或导致它崩溃。 AsyncTasks在一个线程池中运行,最多有5个线程同时执行。虽然可以排队执行许多任务,但一次只能执行其中的5个任务。通过在后台线程池中执行它们,它们不应该对您的应用程序产生任何影响,它们应该运行平稳。

如果您对AsyncTask加载器性能不满意,使用AsyncTaskLoader并不能解决您的问题。 AsyncTaskLoader只需要加载器接口并将其与AsyncTask结合。所以它基本上映射onLoadFinished - > onPostExecute,onStart - > onLoadInBackground。所以它是一样的确切的事情。

我们对我们的应用程序使用相同的图像加载器代码,每当我们尝试加载图像时,都会导致将asynctask放到线程池队列中。在谷歌的例子中,他们将imageview与其异步任务相关联,以便他们可以取消异步任务,如果他们尝试在某种适配器中重新使用imageview。你应该在这里采取类似的策略。您应该将imageview与异步任务关联在后台加载图像。当你有一个没有显示的片段时,你可以循环浏览与该片段相关的图像视图并取消加载任务。简单地使用AsyncTask.cancel()应该工作得很好。

你还应该尝试实现异步图像视图示例阐述的简单图像缓存机制。我们只需创建一个静态哈希映射,它来自url - > weakreference。通过这种方式,图像可以在需要时回收,因为它们只能以弱引用持有。

这里的图像加载的轮廓,我们做

public class LazyLoadImageView extends ImageView { 
     public WeakReference<ImageFetchTask> getTask() { 
     return task; 
    } 

    public void setTask(ImageFetchTask task) { 
     this.task = new WeakReference<ImageFetchTask>(task); 
    } 

    private WeakReference<ImageFetchTask> task; 

     public void loadImage(String url, boolean useCache, Drawable loadingDrawable){ 

     BitmapDrawable cachedDrawable = ThumbnailImageCache.getCachedImage(url); 
     if(cachedDrawable != null){ 
      setImageDrawable(cachedDrawable); 
      cancelDownload(url); 
      return; 
     } 

     setImageDrawable(loadingDrawable); 

     if(url == null){ 
      makeDownloadStop(); 
      return; 
     } 

     if(cancelDownload(url)){ 
      ImageFetchTask task = new ImageFetchTask(this,useCache); 
      this.task = new WeakReference<ImageFetchTask>(task); 
      task.setUrl(url); 
      task.execute(); 
     } 


     ...... 

     public boolean cancelDownload(String url){ 

     if(task != null && task.get() != null){ 

      ImageFetchTask fetchTask = task.get(); 
      String downloadUrl = fetchTask.getUrl(); 

      if((downloadUrl == null) || !downloadUrl.equals(url)){ 
       fetchTask.cancel(true); 
       return true; 
      } else 
       return false; 
     } 

     return true; 

      } 
    } 

因此,只要通过在你的片段的图像视图旋转,然后取消他们当你的片段隐藏和显示他们当你的片段可见。

+0

这是松散的,我已经使用的策略,虽然我使用ModernAsyncTask与自定义池,弱引用和取消未显示的上传。但我认为Loader不仅仅是AsyncTask的简单重用。例如,我可以看到,如果我连续执行多个执行,则后一个调用将取消前一个执行。我想我将不得不花费一个晚上,只是设置示例项目并通过代码来充分了解加载程序的工作原理 – Bostone

+2

加载程序不会执行任何操作。 AsyncTaskLoader是使用AsyncTask的Loader实现。就线程而言,它增加了* AsyncTask中尚未包含的* Nothing。当它需要取消某些东西时,它只会取消AsyncTask,您可以使用AsyncTask来完成自己的任务。 – hackbod

+0

好吧,如果Dianne说的那么:)说 - 我仍然看到多个任务没有排队但取消而只剩下最后一个的行为。这是正确的行为? – Bostone

相关问题