API AsyncTask устарел в Android 11. Каковы альтернативы?
Google не рекомендует Android AsyncTask API в Android 11 и предлагает использовать java.util.concurrent
вместо этого. вы можете ознакомиться с фиксацией здесь
*
* @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> {
Если вы поддерживаете более старую кодовую базу с асинхронными задачами в Android, вам, вероятно, придется изменить ее в будущем. Мой вопрос заключается в том, какой должна быть правильная замена фрагмента кода, показанного ниже, с помощью java.util.concurrent
. Это статический внутренний класс Activity. Я ищу что-то, с чем будет работать 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) ;
}
}
Переведено автоматически
Ответ 1
Вы можете использовать его напрямую Executors
из java.util.concurrent
пакета.
Я также искал информацию об этом и нашел решение в этом посте Android Async API is Deprecated.
К сожалению, в post используется Kotlin, но после небольших усилий я преобразовал его в Java. Итак, вот решение.
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
}
});
}
});
Довольно просто, не так ли? Вы можете упростить это немного больше, если используете Java 8 в своем проекте.
ExecutorService executor = Executors.newSingleThreadExecutor();
Handler handler = new Handler(Looper.getMainLooper());
executor.execute(() -> {
//Background work here
handler.post(() -> {
//UI Thread work here
});
});
Тем не менее, он не может превзойти kotlin по лаконичности кода, но лучше, чем предыдущая версия java.
Надеюсь, это поможет вам. Спасибо
Ответ 2
private WeakReference<MyActivity> activityReference;
Хорошо, что он устарел, потому что WeakReference<Context>
всегда был взломом, а не правильным решением.
Теперь у людей будет возможность очистить свой код.
AsyncTask<String, Void, MyPojo>
Судя по этому коду, Progress
на самом деле не нужен, и есть String
ввод + MyPojo
вывод.
На самом деле это довольно легко выполнить без какого-либо использования 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);
});
});
}
}
Как передать строку? Вот так:
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;
}
}
И
// 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);
});
}
В этом примере использовался однопоточный пул, который хорош для записи в БД (или сериализованных сетевых запросов), но если вам нужно что-то для чтения в БД или нескольких запросов, вы можете рассмотреть следующую конфигурацию исполнителя:
private static final Executor THREAD_POOL_EXECUTOR =
new ThreadPoolExecutor(5, 128, 1,
TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
Ответ 3
Одна из самых простых альтернатив - использовать Thread
new Thread(new Runnable() {
@Override
public void run() {
// do your stuff
runOnUiThread(new Runnable() {
public void run() {
// do onPostExecute stuff
}
});
}
}).start();
Если ваш проект поддерживает JAVA 8, вы можете использовать lambda
:
new Thread(() -> {
// do background stuff here
runOnUiThread(()->{
// OnPostExecute stuff here
});
}).start();
Ответ 4
Согласно документации Android AsyncTask
, он устарел в API 30-го уровня, и вместо него предлагается использовать стандартные утилиты параллелизма java.util.concurrent или Kotlin.
Используя последнее, этого можно добиться довольно просто:
Создайте универсальную функцию расширения на
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
}Используйте функцию с любым,
CoroutineScope
который имеетDispatchers.Main
контекст:В
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"
})
}
}В
Activity
илиFragment
: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 lifecycleScopeAt 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
(viewModelScope
/lifecycleScope
, see implementations above) with Dispatchers.Main
context 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
}
)