Does AsyncTask keep waiting?

The button in one of my actions calls AsyncTask, which updates the main cursor for the ListView SimpleCursorAdapter. Each time I press the button, a new thread for AsyncTask is added, and the task is completed (goes into standby state). If I press the button 5 or more times, 5 AsyncTasks ends there with a "wait" status. Is this normal or do I have a memory leak somewhere?

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

And the code in my onCreate that calls the task (when the activity is first loaded):

 upAdapter = new updateAdapter().execute(); 

And in the onClickListener button:

  // Reset wod_id _wod_id = -1; // Update the underlying SimpleCursorAdapter upAdapter = new updateAdapter().execute(); 

Stacktrace of one of AsyncTask (it is the same for everyone):

 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 
+4
source share
2 answers

AsyncTask under the hood is used by ThreadPoolExecutor . These threads may not disappear because it will be a waste to continue to create and disrupt these threads too often. After a while, if you create more AsyncTasks , you will find that they will stop creating new threads and they will reuse the old ones.

Update to consider some details:

You might think that if there are free threads in the pool, this will not create new ones, but this is not entirely true. The idea is that there are a certain number of threads that can be useful to continue processing asynchronous tasks. This is called the size of the primary pool. In the case of Android AsyncTask they seem to have set it to 5. If you look at the documentation for ThreadPoolExecutor , it says:

When a new task is dispatched when the (Runnable) method is executed and fewer corePoolSize threads are executed, a new thread is created to process the request, even if other worker threads are inactive.

In addition, the maximum size is called the maximum pool size.

+8
source

What @kabuko says is true, but I also think it is good practice to cancel the task before starting a new one. You may have some strange behavior if one of the old tasks continues. Moreover, in your case, you do not want to request your db more than once, it would be useless. You can bind the async task call in the following way:

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

It is also good practice to disable the button and enable it again when the async task is completed.

Anyway, this solution may not be suitable for your architecture, I do not know enough about your code. Hope this helps anyway.

+1
source

Source: https://habr.com/ru/post/1385007/


All Articles