gpt4 book ai didi

Android - 线程池策略,可以用Loader实现吗?

转载 作者:塔克拉玛干 更新时间:2023-11-02 19:22:06 25 4
gpt4 key购买 nike

首先是问题:

  • 我正在开发使用多个 FragmentLists 的应用程序在自定义的 FragmentStatePagerAdapter 中。可能有,此类 fragment 的潜在数量可能在 20 到 40 之间。
  • 每个 fragment 都是一个列表,其中每个项目都可以包含文本或图像。
  • 图像需要从网络异步上传并缓存到临时内存缓存和 SD(如果可用)
  • 当 Fragment 离开屏幕时,应取消所有上传和当前 Activity (而不是暂停)

我的第一个实现遵循众所周知的 image loader code来自谷歌。该代码的问题是它基本上为每个图像创建一个 AsyncTask 实例。在我的例子中,这会很快杀死应用程序。

因为我使用的是 v4 兼容包,所以我认为使用扩展 AsyncTaskLoader 的自定义加载器会对我有所帮助,因为它在内部实现了一个线程池。然而,令我不愉快的是,如果我多次执行此代码,每次后续调用都会中断前一个。假设我的 ListView#getView 方法中有这个:

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

此方法在进入 View 的每个列表项的循环中执行。正如我所说的——接下来的每一次调用都将终止前一次调用。或者至少这是基于 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 会有所帮助,但它似乎没有任何区别。结果,我得到了看似随机的图像,而应用程序从未加载过我所需内容的 1/4。

问题

  • 修复 Loader 以执行我想要的操作的方法是什么(有什么方法吗?)
  • 如果不是,创建 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;
}

}

}

最佳答案

好的,所以首先要做的是。 android 附带的 AsyncTask 不应淹没您的应用程序或导致其崩溃。 AsyncTasks 在线程池中运行,其中最多有 5 个线程同时实际执行。虽然您可以排队执行许多任务,但一次只有 5 个任务在执行。通过在后台线程池中执行它们,它们根本不会对您的应用产生任何影响,它们应该可以顺利运行。

如果您对 AsyncTask 加载程序性能不满意,使用 AsyncTaskLoader 将无法解决您的问题。 AsyncTaskLoader 只是采用加载程序接口(interface)并将其与 AsyncTask 结合。所以它本质上是映射 onLoadFinished -> onPostExecute,onStart -> onLoadInBackground。所以这是完全相同的事情。

我们为我们的应用程序使用相同的图像加载器代码,每次我们尝试加载图像时都会将异步任务放入线程池队列中。在谷歌的示例中,他们将 imageview 与其异步任务相关联,以便他们可以在尝试在某种适配器中重用 imageview 时取消异步任务。你应该在这里采取类似的策略。您应该将 imageview 与正在后台加载图像的异步任务相关联。当您有一个未显示的 fragment 时,您可以循环浏览与该 fragment 关联的 ImageView 并取消加载任务。简单地使用 AsyncTask.cancel() 应该就足够了。

您还应该尝试实现异步 ImageView 示例阐明的简单图像缓存机制。我们只需创建一个从 url -> weakreference 开始的静态 HashMap 。这样,图像可以在需要时被回收,因为它们仅在弱引用下保留。

这是我们所做的图像加载的概述

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;

}
}

因此,只需旋转 fragment 中的 ImageView ,然后在 fragment 隐藏时取消它们,并在 fragment 可见时显示它们。

关于Android - 线程池策略,可以用Loader实现吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8001567/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com