Android AsyncTask thread limits?

I am developing an application where I need to update some information every time a user logs in, I also use the database on the phone. For all these operations (updating, extracting data from db, etc.) I use async tasks. Since until now I did not understand why I should not use them, but recently I have experienced that if I perform some operations, some of my async tasks just stop at the preliminary execution and do not switch to doInBackground. It was too weird to leave it like that, so I developed another simple application to check what happened. And, strangely enough, I get the same behavior when the number of asynchronous tasks reaches 5, 6th - with preliminary execution.

Does android have asyncTasks limitations in Activity / App? Or is it just a mistake and needs to be reported? Has anyone experienced the same problem and possibly found a workaround?

Here is the code:

Just create 5 of these threads to run in the background:

private class LongAsync extends AsyncTask<String, Void, String> { @Override protected void onPreExecute() { Log.d("TestBug","onPreExecute"); isRunning = true; } @Override protected String doInBackground(String... params) { Log.d("TestBug","doInBackground"); while (isRunning) { } return null; } @Override protected void onPostExecute(String result) { Log.d("TestBug","onPostExecute"); } } 

And then create this thread. It will go into preExecute and hang (it will not go to doInBackground).

 private class TestBug extends AsyncTask<String, Void, String> { @Override protected void onPreExecute() { Log.d("TestBug","onPreExecute"); waiting = new ProgressDialog(TestActivity.this); waiting.setMessage("Loading data"); waiting.setIndeterminate(true); waiting.setCancelable(true); waiting.show(); } @Override protected String doInBackground(String... params) { Log.d("TestBug","doInBackground"); return null; } @Override protected void onPostExecute(String result) { waiting.cancel(); Log.d("TestBug","onPostExecute"); } } 
+88
android multithreading android-asynctask
Mar 11 2018-12-12T00:
source share
3 answers

All AsyncTasks are managed inside a common (static) ThreadPoolExecutor and LinkedBlockingQueue . When you call execute in AsyncTask, ThreadPoolExecutor will execute it when it is ready in the future.

"When am I ready?" The behavior of a ThreadPoolExecutor controlled by two parameters: size and maximum pool size . If the threads of the main thread of the pool are currently active, and a new task arrives, the executor will create a new thread and execute it immediately. If there are at least the main threads of the pool size, he will try to queue the task and wait until he has access to the free stream (i.e. until another task is completed). If it is not possible to queue a task (a queue can have a maximum capacity), it will create a new thread (up to the maximum pool threads) for work orders that will be executed. Non-core idle threads can eventually be decommissioned according to the keep-alive timeout setting.

Before Android 1.6, the size of the main pool was 1, and the maximum pool size was 10. With Android 1.6, the size of the main pool is 5 and the maximum pool size is 128. The queue size is 10 in both cases, the keep-alive timeout was 10 seconds to 2, 3 and 1 second since then.

With all this in mind, it is now clear why AsyncTask will only perform 5/6 of your tasks. The 6th task is queued until one of the other tasks is completed. This is a very good reason why you should not use AsyncTasks for lengthy operations - this will prevent other AsyncTasks from starting.

For completeness, if you repeat the exercise with more than 6 tasks (for example, 30), you will see that more than 6 will enter doInBackground when the queue is full and the artist is pressed to create more workflows. If you are doing a long-term task, you will see that 20/30 become active, and another 10 are in line.

+197
Mar 11 '12 at 11:15
source share

@antonyt has the correct answer, but if you are looking for a simple solution, you can check the needle.

With it, you can determine your own thread pool size and, unlike AsyncTask , it works the same on all versions of Android. With it, you can say things like:

 Needle.onBackgroundThread().withThreadPoolSize(3).execute(new UiRelatedTask<Integer>() { @Override protected Integer doWork() { int result = 1+2; return result; } @Override protected void thenDoUiRelatedWork(Integer result) { mSomeTextView.setText("result: " + result); } }); 

or things like

 Needle.onMainThread().execute(new Runnable() { @Override public void run() { // eg change one of the views } }); 

He can do a lot more. Check it out on github .

+9
Aug 29 '14 at 19:55
source share

Update : starting from API 19, the size of the main thread pool has been changed to reflect the number of processors on the device with a minimum value of 2 and a maximum of 4 at startup, increasing to a maximum value of CPU * 2 +1 - Link

 // We want at least 2 threads and at most 4 threads in the core pool, // preferring to have 1 less than the CPU count to avoid saturating // the CPU with background work private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4)); private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; 

Also note that although the default executor of AsyncTask is sequential (performs one task at a time and in the order in which they arrive), using the method

 public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) 

You can provide the Contractor to carry out your tasks. You can provide THREAD_POOL_EXECUTOR under the hood performer, but without serializing tasks, or you can even create your own Performer and provide it here. However, pay close attention to the warning in Javadocs.

Warning. Resolving multiple tasks running in parallel from a thread pool is usually not what is required because the order in which they work is not defined. For example, if these tasks are used to change the general state (for example, to write a file due to a button click), there are no guarantees regarding the order of modifications. Without careful work in rare cases when a newer version of the data can be overwritten by an older version, which leads to unclear problems of data loss and stability. Such changes are best done in serial mode; to ensure that such work is serialized regardless of the platform version, you can use this function with SERIAL_EXECUTOR.

Another thing to note is that both structures provided by Executors THREAD_POOL_EXECUTOR and its serial version SERIAL_EXECUTOR (which is the default for AsyncTask) are static (class-level constructs) and are therefore shared across all AsyncTask instances ( s) in your application.

+5
Nov 26 '16 at
source share



All Articles