The AsyncTask API is deprecated in Android 11. What are the alternatives?
Google is deprecating Android AsyncTask API in Android 11 and suggesting to use java.util.concurrent
instead. you can check out the commit here
*
* @deprecated Use the standard <code>java.util.concurrent</code> or
* <a href="https://developer.android.com/topic/libraries/architecture/coroutines">
* Kotlin concurrency utilities</a> instead.
*/
@Deprecated
public abstract class AsyncTask<Params, Progress, Result> {
If you’re maintaining an older codebase with asynchronous tasks in Android, you’re likely going to have to change it in future. My question is that what should be proper replacement of the code snippet shown below using java.util.concurrent
. It is a static inner class of an Activity. I am looking for something that will work with minSdkVersion 16
private static class LongRunningTask extends AsyncTask<String, Void, MyPojo> {
private static final String TAG = MyActivity.LongRunningTask.class.getSimpleName();
private WeakReference<MyActivity> activityReference;
LongRunningTask(MyActivity context) {
activityReference = new WeakReference<>(context);
}
@Override
protected MyPojo doInBackground(String... params) {
// Some long running task
}
@Override
protected void onPostExecute(MyPojo data) {
MyActivity activity = activityReference.get();
activity.progressBar.setVisibility(View.GONE);
populateData(activity, data) ;
}
}
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>());
You can directly use Executors from java.util.concurrent
package.
I also searched about it and I found a solution in this Android Async API is Deprecated post.
Unfortunately the post is using Kotlin, but after a little effort I have converted it to Java. So here is the solution.
ExecutorService executor = Executors.newSingleThreadExecutor();
Handler handler = new Handler(Looper.getMainLooper());
executor.execute(new Runnable() {
@Override
public void run() {
//Background work here
handler.post(new Runnable() {
@Override
public void run() {
//UI Thread work here
}
});
}
});
Pretty simple right? You can simplify it little more if you are using Java8 in your project.
ExecutorService executor = Executors.newSingleThreadExecutor();
Handler handler = new Handler(Looper.getMainLooper());
executor.execute(() -> {
//Background work here
handler.post(() -> {
//UI Thread work here
});
});
Still, it cannot defeat kotlin terms of conciseness of the code, but better than the previous java version.
Hope this will help you. Thank You
One of the simplest alternative is to use Thread
new Thread(new Runnable() {
@Override
public void run() {
// do your stuff
runOnUiThread(new Runnable() {
public void run() {
// do onPostExecute stuff
}
});
}
}).start();
If your project supports JAVA 8, you can use lambda
new Thread(() -> {
// do background stuff here
runOnUiThread(()->{
// OnPostExecute stuff here
});
}).start();
According to the Android documentation AsyncTask
was deprecated in API level 30 and it is suggested to use the standard java.util.concurrent or Kotlin concurrency utilities instead.
Using the latter it can be achieved pretty simple:
-
Create generic extension function on
CoroutineScope
:fun <R> CoroutineScope.executeAsyncTask( onPreExecute: () -> Unit, doInBackground: () -> R, onPostExecute: (R) -> Unit ) = launch { onPreExecute() // runs in Main Thread val result = withContext(Dispatchers.IO) { doInBackground() // runs in background thread without blocking the Main Thread } onPostExecute(result) // runs in Main Thread }
-
Use the function with any
CoroutineScope
:-
In
ViewModel
:class MyViewModel : ViewModel() { fun someFun() { viewModelScope.executeAsyncTask(onPreExecute = { // ... runs in Main Thread }, doInBackground = { // ... runs in Worker(Background) Thread "Result" // send data to "onPostExecute" }, onPostExecute = { // runs in Main Thread // ... here "it" is the data returned from "doInBackground" }) } }
-
In
Activity
orFragment
:lifecycleScope.executeAsyncTask(onPreExecute = { // ... runs in Main Thread }, doInBackground = { // ... runs in Worker(Background) Thread "Result" // send data to "onPostExecute" }, onPostExecute = { // runs in Main Thread // ... here "it" is the data returned from "doInBackground" })
To use
viewModelScope
orlifecycleScope
add next line(s) to dependencies of the app's build.gradle file:implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$LIFECYCLE_VERSION" // for viewModelScope implementation "androidx.lifecycle:lifecycle-runtime-ktx:$LIFECYCLE_VERSION" // for lifecycleScope
At the time of writing
final LIFECYCLE_VERSION = "2.3.0-alpha05"
-
UPDATE:
Also we can implement progress updating using onProgressUpdate
function:
fun <P, R> CoroutineScope.executeAsyncTask(
onPreExecute: () -> Unit,
doInBackground: suspend (suspend (P) -> Unit) -> R,
onPostExecute: (R) -> Unit,
onProgressUpdate: (P) -> Unit
) = launch {
onPreExecute()
val result = withContext(Dispatchers.IO) {
doInBackground {
withContext(Dispatchers.Main) { onProgressUpdate(it) }
}
}
onPostExecute(result)
}
Using any CoroutineScope
(see implementations above) we can call it:
someScope.executeAsyncTask(
onPreExecute = {
// ... runs in Main Thread
}, doInBackground = { publishProgress: suspend (progress: Int) -> Unit ->
// ... runs in Background Thread
// simulate progress update
publishProgress(50) // call `publishProgress` to update progress, `onProgressUpdate` will be called
delay(1000)
publishProgress(100)
"Result" // send data to "onPostExecute"
}, onPostExecute = {
// runs in Main Thread
// ... here "it" is a data returned from "doInBackground"
}, onProgressUpdate = {
// runs in Main Thread
// ... here "it" contains progress
}
)
Use this class to execute background task in Background Thread this class is work for all android API version include Android 11 also this code is same work like AsyncTask with doInBackground and onPostExecute methods
public abstract class BackgroundTask {
private Activity activity;
public BackgroundTask(Activity activity) {
this.activity = activity;
}
private void startBackground() {
new Thread(new Runnable() {
public void run() {
doInBackground();
activity.runOnUiThread(new Runnable() {
public void run() {
onPostExecute();
}
});
}
}).start();
}
public void execute(){
startBackground();
}
public abstract void doInBackground();
public abstract void onPostExecute();
}
After copying the above class, you can then use it with this:
new BackgroundTask(MainActivity.this) {
@Override
public void doInBackground() {
//put you background code
//same like doingBackground
//Background Thread
}
@Override
public void onPostExecute() {
//hear is result part same
//same like post execute
//UI Thread(update your UI widget)
}
}.execute();