Asynctask.Executeonexecutor() Before API Level 11

AsyncTask.executeOnExecutor() before API Level 11

My intense tasks are loosely coupled and the execution order does not matter, by doing this way, a single thread is allocated to run a list of intense tasks.

AsyncTask presently uses a thread pool with several threads. In the future, it may be restricted to a single thread -- Google has hinted that this will be the case.

wonder if there is a workaround that I can achieve the same behavior under API level 11.

The default behavior is the behavior you want. If you examine the source code to AsyncTask, you will see that as of Gingerbread, it used a thread pool with a minimum of 5 threads and a maximum of 128.

Now, bear in mind that the vast majority of Android devices in use today are single-core. Hence, unless your "intense tasks" are intensely not doing much but blocking on network I/O, you do not want to be doing them in parallel, as context switches between threads will simply slow you down further.

The AsyncTask API is deprecated in Android 11. What are the alternatives?

private WeakReference<MyActivity> activityReference;

Good riddance that it's deprecated, because the WeakReference<Context> was always a hack, and not a proper solution.

Now people will have the opportunity to sanitize their code.


AsyncTask<String, Void, MyPojo> 

Based on this code, Progress is actually not needed, and there is a String input + MyPojo output.

This is actually quite easy to accomplish without any use of AsyncTask.

public class TaskRunner {
private final Executor executor = Executors.newSingleThreadExecutor(); // change according to your requirements
private final Handler handler = new Handler(Looper.getMainLooper());

public interface Callback<R> {
void onComplete(R result);
}

public <R> void executeAsync(Callable<R> callable, Callback<R> callback) {
executor.execute(() -> {
final R result = callable.call();
handler.post(() -> {
callback.onComplete(result);
});
});
}
}

How to pass in the String? Like so:

class LongRunningTask implements Callable<MyPojo> {
private final String input;

public LongRunningTask(String input) {
this.input = input;
}

@Override
public MyPojo call() {
// Some long running task
return myPojo;
}
}

And

// in ViewModel
taskRunner.executeAsync(new LongRunningTask(input), (data) -> {
// MyActivity activity = activityReference.get();
// activity.progressBar.setVisibility(View.GONE);
// populateData(activity, data) ;

loadingLiveData.setValue(false);
dataLiveData.setValue(data);
});

// in Activity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.main_activity);

viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
viewModel.loadingLiveData.observe(this, (loading) -> {
if(loading) {
progressBar.setVisibility(View.VISIBLE);
} else {
progressBar.setVisibility(View.GONE);
}
});

viewModel.dataLiveData.observe(this, (data) -> {
populateData(data);
});
}

This example used a single-threaded pool which is good for DB writes (or serialized network requests), but if you want something for DB reads or multiple requests, you can consider the following Executor configuration:

private static final Executor THREAD_POOL_EXECUTOR =
new ThreadPoolExecutor(5, 128, 1,
TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());

AsyncTask Thread_Pool_Executor on Android API 11

Step #1: Set your build target (e.g., Project > Properties > Android in Eclipse) to API Level 11 or higher.

Step #2: Use Build.VERSION.SDK_INT to determine your API level, to use executeOnExecutor() only on newer Android devices.

For example, you could have this static utility method somewhere:

  @TargetApi(Build.VERSION_CODES.HONEYCOMB)
static public <T> void executeAsyncTask(AsyncTask<T, ?, ?> task, T... params) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
}
else {
task.execute(params);
}
}

Then, to execute a task, just call executeAsyncTask(), passing it your AsyncTask instance and whatever parameters you want passed into your doInBackground() method. executeAsyncTask() will choose executeOnExecutor() on API Level 11+ or use execute() on older devices.

AsyncTask execute() or executeOnExecutor()?

How does .execute execute tasks by default (in serial or in parallel).

Before API level 11: parallel.

API level 11 and up: serial.

which should be used for new SDKs >16 (executeOnExecuter ?)

Depends on your requirements. Use execute() if you're happy with the default executor. Use an explicit executor if you're not.

Is it a good practice to use parallel execution (THREAD_POOL_EXECUTOR) for tasks rather than serial even if it doesn't matter for the application or does that depends on the number of async tasks that will be executed?

Async tasks should only be used for relative short backround operations. Quoting AsyncTask documentation:

AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent package such as Executor, ThreadPoolExecutor and FutureTask.

While the async task is running, the executor thread cannot execute other tasks. On a serial executor with only one executor thread it is easier to detect problems when your tasks run for too long. On a parallel executor detecting such problems takes more simultaneous long-running tasks.

Therefore, if you really need to switch to a parallel executor, you're probably better off revisiting your design.

Async task.. cannot call executeOnExecutor()

Set your build target to API Level 11 or higher. Note that "build target" != "target SDK" (android:targetSdkVersion). "Build target" is set in Project > Properties > Android on Eclipse, or project.properties for command-line builds.

For conditional use of executeOnExecutor(), another approach is to use a separate helper method:

  @TargetApi(11)
static public <T> void executeAsyncTask(AsyncTask<T, ?, ?> task,
T... params) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
}
else {
task.execute(params);
}
}

You would then use this method to kick off your tasks:

 executeAsyncTask(new MyTask(), param1, param2);

(for however many parameters you wanted to pass to execute())

Error message : Field require API Level 11 (current min is 9) : android .os.AsynTask#THREAD_POOL_EXECUTOR

That method (executeOnExecutor()) and that field (THREAD_POOL_EXECUTOR) were added in API Level 11. Your minSdkVersion is 9, and so some devices that you run on will not have that method or field, and you will crash.

Either raise your minSdkVersion to 11, or only use executeOnExecutor() on new-enough devices:

  @TargetApi(Build.VERSION_CODES.HONEYCOMB)
static public <T> void executeAsyncTask(AsyncTask<T, ?, ?> task,
T... params) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
}
else {
task.execute(params);
}
}


Related Topics



Leave a reply



Submit