AsyncTask продолжает ждать? - PullRequest
4 голосов
/ 07 декабря 2011

Кнопка в одном из моих действий вызывает AsyncTask, который обновляет базовый Курсор для SimpleCursorAdapter ListView.Каждый раз, когда я нажимаю кнопку, добавляется новый поток для AsyncTask, и задача завершается (переходит в состояние ожидания).Если я нажму кнопку 5 или более раз, 5 AsyncTasks окажутся там со статусом «ожидания».Это нормально или у меня где-то есть утечка памяти?

AsyncTask

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

И в кнопке onClickListener:

            // Reset wod_id
            _wod_id = -1;

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

Stacktrace одного из 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 

Ответы [ 2 ]

8 голосов
/ 07 декабря 2011

AsyncTask под капотом используется ThreadPoolExecutor.Эти потоки могут не исчезнуть на некоторое время, потому что было бы бесполезно продолжать создавать и разрушать эти потоки слишком часто.Через некоторое время, если вы создадите больше AsyncTasks, вы обнаружите, что оно прекратит создавать новые потоки и будет повторно использовать старые.

Обновление с учетом некоторых деталей:

Можно подумать, что если в пуле есть свободные потоки, они не будут создавать новые, но это не совсем так.Идея состоит в том, что есть определенное количество потоков, которые полезно иметь для обработки асинхронных задач.Это называется размером основного пула.В случае AsyncTask в Android они, похоже, установили его на 5. Если вы посмотрите документацию для ThreadPoolExecutor, то там написано:

Когда в метод execute выполняется (Runnable) новая задачаи работает меньше потоков corePoolSize, создается новый поток для обработки запроса, даже если другие рабочие потоки бездействуют.

Существует также максимально соответствующий максимальный размер пула.

1 голос
/ 07 декабря 2011

То, что говорит @kabuko, верно, но я также думаю, что это хорошая практика - отменить задание перед началом нового.У вас может быть странное поведение, если одна из старых задач будет продолжаться.Более того, в вашем случае вы не хотите запрашивать вашу базу данных более одного раза, это будет бесполезно.Вы можете заключить вызов асинхронной задачи в такой метод:* В любом случае, это решение не подходит для вашей архитектуры, я не знаю достаточно о вашем коде.Надеюсь, это все равно поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...