【问题标题】:AsyncTask keeps waiting?AsyncTask 一直在等待?
【发布时间】:2011-12-06 21:53:57
【问题描述】:

我的一个活动中的一个按钮调用了一个 AsyncTask,它更新了 ListView 的 SimpleCursorAdapter 的基础游标。每次单击该按钮时,都会为 AsyncTask 添加一个新线程并且任务完成(进入“等待”状态)。如果我单击该按钮 5 次或更多次,则 5 个 AsyncTasks 最终会以“等待”状态坐在那里。这是正常的还是我的某个地方有内存泄漏?

异步任务

private class updateAdapter extends AsyncTask<Void, Void, Void> {

    @Override
    protected Void doInBackground(Void... params) {
        // Open database connection
        if(_db == null || !_db.isOpen()) _db = new DatabaseWrapper(ActivityShowWOD.this).getWritableDatabase();
        Cursor WODcursor;

        // Check if a wod_id is set
        if(_wod_id == -1) {
            // Grab filters from preferences and at the same time build SQLselection string
            SharedPreferences prefs = getSharedPreferences("Preferences", 0);
            String[] filterNames = getResources().getStringArray(R.array.filters_values);
            boolean[] filterValues = new boolean[filterNames.length];
            String SQLselection = "";

            for (int i = 0; i < filterNames.length; i++) {
                filterValues[i] = prefs.getBoolean(filterNames[i], false);

                // Build SQL query
                if(filterValues[i] == true) {
                    SQLselection += filterNames[i] + " = 1 OR " +  filterNames[i] + " = 0";
                } else {
                    SQLselection += filterNames[i] + " = 0";
                }

                // Add an "AND" if there are more filters 
                if(i < filterNames.length - 1) SQLselection += " AND ";
            }

            // Get all WODs matching filter preferences 
            WODcursor = _db.query(DatabaseConstants.TBL_WORKOUTS, 
                                      new String[] { DatabaseConstants.WORKOUTS_ID, DatabaseConstants.WORKOUTS_NAME,
                                                     DatabaseConstants.WORKOUTS_NOTES, DatabaseConstants.WORKOUTS_CFID }, 
                                      SQLselection, null, null, null, null);

            // Move the Cursor to a random position
            Random rand = new Random();
            WODcursor.moveToPosition(rand.nextInt(WODcursor.getCount()));

            // Store wod_id
            _wod_id = WODcursor.getInt(WODcursor.getColumnIndex(DatabaseConstants.WORKOUTS_ID));
        } else {
            // Get the cursor from the wod_id
            WODcursor = _db.query(DatabaseConstants.TBL_WORKOUTS, 
                                 new String[] { DatabaseConstants.WORKOUTS_ID, DatabaseConstants.WORKOUTS_NAME,
                                                DatabaseConstants.WORKOUTS_NOTES, DatabaseConstants.WORKOUTS_CFID }, 
                                 DatabaseConstants.WORKOUTS_ID + " = " + _wod_id, null, null, null, null);

            WODcursor.moveToFirst();
        }

        // Store WOD information into class instance variables and close cursor
        _wod_cfid = WODcursor.getInt(WODcursor.getColumnIndex(DatabaseConstants.WORKOUTS_CFID));
        _wod_name = WODcursor.getString(WODcursor.getColumnIndex(DatabaseConstants.WORKOUTS_NAME));
        _wod_notes = WODcursor.getString(WODcursor.getColumnIndex(DatabaseConstants.WORKOUTS_NOTES));
        WODcursor.close();

        // Return all exercises pertaining to this WOD
        _excCursor = _db.query(DatabaseConstants.TBL_EXERCISES, 
                              new String[] { DatabaseConstants.EXERCISES_ID, DatabaseConstants.EXERCISES_EXERCISE,
                                             DatabaseConstants.EXERCISES_REPS, DatabaseConstants.EXERCISES_NOTES }, 
                              DatabaseConstants.EXERCISES_WOD_ID + " = " + _wod_id, null, null, null, 
                              DatabaseConstants.EXERCISES_ID + " ASC");
        return null;
    }

    @Override
    protected void onPostExecute(Void result) {
        _adapter.changeCursor(_excCursor);
        _adapter.notifyDataSetChanged();
        _WODlist.setOnItemClickListener(new WODlistClickListener());
    }

}

我的 onCreate 中调用任务的代码(首次加载活动时):

upAdapter = new updateAdapter().execute();

在按钮上点击监听:

            // Reset wod_id
            _wod_id = -1;

            // Update the underlying SimpleCursorAdapter
            upAdapter = new updateAdapter().execute();

其中一个 AsyncTask 的堆栈跟踪(所有这些都相同):

Object.wait(long, int) line: not available [native method]  
Thread.parkFor(long) line: 1535 
LangAccessImpl.parkFor(long) line: 48   
Unsafe.park(boolean, long) line: 317    
LockSupport.park() line: 131    
AbstractQueuedSynchronizer$ConditionObject.await() line: 1996   
LinkedBlockingQueue.take() line: 359    
ThreadPoolExecutor.getTask() line: 1001 
ThreadPoolExecutor.runWorker(ThreadPoolExecutor$Worker) line: 1061  
ThreadPoolExecutor$Worker.run() line: 561   
Thread.run() line: 1096 

【问题讨论】:

  • 我们可以看看你正在运行的一些代码吗?您是在每个 AsyncTask 上调用 execute() 还是只是创建对象?
  • 你能为那些正在睡觉的AsynTask线程发布完整的堆栈跟踪吗?根据我的经验,应该不会有很多人停放。
  • 另外,您在哪个 Android 版本上进行测试?
  • @inazaruk Android 2.2。如何获取 AsyncTask 线程的堆栈跟踪?
  • @@soren.qvist,从堆栈跟踪中可以清楚地看出@kabuko 是正确的。

标签: java android android-asynctask


【解决方案1】:

AsyncTask 在后台使用ThreadPoolExecutor。这些线程可能暂时不会消失,因为过于频繁地创建和拆除这些线程是一种浪费。一段时间后,如果您创建更多AsyncTasks,您会发现它会停止创建新线程并重新使用旧线程。

更新以解决一些细节:

您会认为如果池中有空闲线程,它就不会创建新线程,但事实并非如此。这个想法是有一定数量的线程可以用来继续处理异步任务。这称为核心池大小。在 Android 的 AsyncTask 案例中,他们似乎已将其设置为 5。如果您查看 ThreadPoolExecutor 的文档,它会说:

当在方法 execute(Runnable) 中提交了一个新任务,并且运行的线程少于 corePoolSize 时,即使其他工作线程处于空闲状态,也会创建一个新线程来处理该请求。

还有一个最大值恰当地称为最大池大小。

【讨论】:

  • 确实如此,但是如果池中有一些空闲线程,为什么还要创建新线程呢?实际上不应该那样做。
  • 添加了更多细节来解决您的问题。
  • 看来你是对的。 AsyncTask 确实有 5 作为其核心池大小(请参阅 goo.gl/i2QMa)。
  • 谢谢大家。我总是可以依靠 StackOverflow :)
【解决方案2】:

@kabuko 说的是真的,但我也认为在开始新任务之前取消任务是一个好习惯。如果一项旧任务继续进行,您可能会有一些奇怪的行为。此外,在您的情况下,您不想多次查询您的数据库,这将毫无用处。您可以将异步任务的调用包装在这样的方法中:

AsyncDataLoading loaderTask = null;

private void runTask(){
    if (loaderTask!=null && loaderTask.getStatus().compareTo(Status.FINISHED)!=0) {
        loaderTask.cancel(true);
    }
    loaderTask = new AsyncDataLoading();
    loaderTask.execute();
} 

在异步任务完成时禁用按钮并重新启用它也是一个好习惯。

无论如何,这个解决方案不适合您的架构,我对您的代码了解不够。无论如何,希望它会有所帮助。

【讨论】:

  • 感谢您的建议,我不确定我是否理解您发布的代码(AsyncTask 对我来说是全新的),AsyncDataLoading 似乎不是来自 Android 库?
  • 没问题。 AsyncDataLoading 只是扩展 AsyncTask 的类的名称,在您的情况下为“updateAdapter”...
  • 哎呀.. 再次通读您的代码,现在这很有意义。谢谢。
猜你喜欢
  • 2016-04-22
  • 1970-01-01
  • 2020-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-02
相关资源
最近更新 更多