【问题标题】:AsyncTask performance issue on many AsyncTasks spawning.许多 AsyncTask 产生的 AsyncTask 性能问题。
【发布时间】:2010-09-19 09:30:38
【问题描述】:

我正在尝试为来自设备和网络的图像创建一个惰性图像加载器,用于ListView。 我正在考虑使用什么以及如何使用,一方面我可以使用一个线程来汇集我的请求(始终运行,我可以附加一个视图和一个适配器,它将为我处理图像加载),缓存我已经加载图像并在加载之前检查图像的可见性,这样我就不会做不必要的工作了。

我又想到了使用AsyncTask,就像论坛中许多人建议的那样。不过有一个缺点。我看到很多人使用new MyTask().execute(urls); 如果我想开始加载和停止按需加载图像,这会出现问题。 如果我对每个图像使用异步任务,那么我需要为每个图像创建新的异步任务,这是很多“新”任务,我可以使用一个池,但如果有太多异步任务被卡住,我仍然会创建大约 150- 200 个 asyc 任务,对我来说太多了……

你们怎么看?我认为线程会在这里做得更好:

  1. 继续跑直到被杀
  2. 尝试从队列中获取作业,如果没有作业,请稍候。
  3. 如果有作业,请获取并开始处理。
  4. 每个请求都单独处理,串行并阻塞线程。
  5. Once do 继续使用 '2'。
  6. 适配器对需要显示的视图使用startLoadingImage() 完成的每个入队都会创建一个新作业并在等待锁上调用 notify。

如果我想要多个并行的 GET\POST 请求,我可以使用线程池优化此代码。 此外,我正在缓存我已经下载\加载的图像,以便在下次访问时快速加载。这个想法是最小化 GC 和列表滞后。

【问题讨论】:

    标签: android listview android-asynctask lazy-loading


    【解决方案1】:

    我实现了这样的东西:

    /** Contains all the pending requests for thumbnails. */
    private LinkedList<Uri> mPendingThumbnailRequests = new LinkedList<Uri>();
    
    private ThumbnailGetter mThmGetter = null;
    /**
     * Asynchronous process for retrieving thumbnails from Uris.
     */
    private class ThumbnailGetter extends AsyncTask<Uri, Integer, Uri> {
        private final String LOG_TAG = ThumbnailGetter.class
                .getSimpleName();
        /** The Uri beeing processed */
        private Uri mUri = null;
        /*
         * (non-Javadoc)
         * 
         * @see android.os.AsyncTask#doInBackground(Params[])
         */
        @Override
        protected Uri doInBackground(Uri... uris) {
            // We process Uris one after another... so the Array contains
            // only one Uri.
            mUri = uris[0];
            // Let the ThumbnailLoader do the job.
            Uri result = ItemsLoader.getThumbnail(mContext, mUri);
            return result;
        }
        /*
         * (non-Javadoc)
         * 
         * @see android.os.AsyncTask#onPostExecute(java.lang.Object)
         */
        @Override
        protected void onPostExecute(Uri result) {
            super.onPostExecute(result);
            // Give the retrieved thumbnail to the adapter...
            mImageAdapter.updateThumbUri(mUri, result);
            // then process any other pending thumbnail request.
            if (!mPendingThumbnailRequests.isEmpty()) {
                mThmGetter = new ThumbnailGetter();
                mThmGetter.execute(mPendingThumbnailRequests.poll());
             }
         }
    }
    

    然后我添加要加载的 Uris:

    if (!mPendingThumbnailRequests.contains(imageUri)) {
        mPendingThumbnailRequests.offer(imageUri);
        if (mThmGetter == null
                || mThmGetter.getStatus() == AsyncTask.Status.FINISHED) {
            // If the previous instance of the thumbnail getter has
            // finished, start a new one.
            mHandler.sendEmptyMessage(MSG_SHOW_INDETERMINATE_PROGRESS);
            mThmGetter = new ThumbnailGetter();
            mThmGetter.execute(mPendingThumbnailRequests.poll());
        }
    }
    

    这甚至可以让您使用 mPendingThumbnailRequests.remove() 取消请求

    完整的实现在这里: http://code.google.com/p/emailalbum/source/browse/EmailAlbumAndroid/trunk/src/com/kg/emailalbum/mobile/creator/SelectPictures.java

    【讨论】:

    • 这段代码绝对不错,但如果我考虑一个包含图像的列表,它以未知的速度和批次加载它们(因为用户可以随心所欲地滚动),你会得到很多 AyncTasks创建的线程与您自己的线程相反,该线程创建一次并休眠,直到您将新对象排入队列,确实这对他来说是更多的内存,但如果您使用自己的池并避免在run() 函数。
    • 创建的 AsyncTask 不会很多,因为我们会重用现有的,如果有的话。好吧,实际上,当用户开始滚动并且有一些缩略图要检索时,它会重新创建一个新的……但这只是滚动开始时的一次分配问题。我从来不喜欢睡觉的线程,更担心那些线程永远不会被停止或被复制。
    • 嘿,我刚刚发现你是对的,我为每个 Uri 创建一个新的 AsyncTask ......我会处理这个。谢谢你给我看这个。 ;-)
    • 您不需要同步访问mPendingThumbnailRequests 吗?似乎某些 AsyncTask 可能会执行两次(或零次,具体取决于何时设置 AsyncTask.Status.FINISHED)。
    【解决方案2】:

    我认为您正在做过早的优化。只需以尽可能最快的方式实施您需要的东西,之后您总是可以改进实施。另外,为什么您需要同时启动 200 个 AsyncTask?我认为您不会在一个屏幕上显示所有图像(如果是 ListView,为什么即使用户永远无法滚动到列表末尾,为什么还要加载所有图像?)。

    【讨论】:

    • 不,那是用户可以滚动到最后的点,所有图像都将预加载默认图像,如“等待加载”等,一旦加载图像,就会通知视图并且图像放置在右视图中。一旦它被缓存,加载的响应时间很快,所以不需要异步执行。关于 200 个异步任务,这只是我为什么不能使用它的一个例子......
    • 我这样做了,现在如果我滚动太快并且在我正常滚动时任务完成无序,我会得到 java.util.concurrent.RejectedExecutionException。首先尝试有效地完成它可能是最简单的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-24
    • 1970-01-01
    相关资源
    最近更新 更多