AsyncTask
- отличная вещь для запуска сложных задач в другом потоке.
Но когда происходит изменение ориентации или другое изменение конфигурации, пока AsyncTask
все еще работает, текущий Activity
уничтожается и перезапускается. И поскольку экземпляр AsyncTask
связан с этим действием, он терпит неудачу и вызывает окно сообщения "принудительное закрытие".
Итак, я ищу какую-то "лучшую практику", чтобы избежать этих ошибок и предотвратить сбой AsyncTask.
То, что я видел до сих пор:
- Отключить изменения ориентации. (Не уверен, что вы должны справиться с этим.)
- Предоставление возможности этой задачи выжить и обновить ее с помощью нового экземпляра активности через
onRetainNonConfigurationInstance
- Просто отмените задачу, когда
Activity
будет уничтожен и перезагрузится, когда снова будет создан Activity
.
- Привязка задачи к классу приложения вместо экземпляра активности.
- Некоторые методы, используемые в проекте "shelves" (через onRestoreInstanceState)
Некоторые примеры кода:
Android AsyncTasks во время вращения экрана, часть I и Часть II
ShelvesActivity.java
Можете ли вы помочь мне найти наилучший подход, который лучше всего решает проблему и ее легко реализовать? Сам код также важен, поскольку я не знаю, как правильно его решить.
Ответ 1
Do НЕ использовать android:configChanges
для решения этой проблемы. Это очень плохая практика.
Сделайте НЕ использование Activity#onRetainNonConfigurationInstance()
. Это менее модульно и не подходит для приложений на основе Fragment
.
Вы можете прочитать мою статью, описывая, как обрабатывать изменения конфигурации, используя сохраненные Fragment
s. Это решает проблему сохранения AsyncTask
по изменению поворота красиво. Вам нужно разместить AsyncTask
внутри Fragment
, вызвать setRetainInstance(true)
на Fragment
и сообщить о ходе выполнения AsyncTask
к нему Activity
через сохраненный Fragment
.
Ответ 2
Я обычно решаю это, предлагая мои трансляции AsyncTasks Intents в обратном вызове .onPostExecute(), поэтому они не изменяют действие, которое их запускало напрямую. Мероприятия прослушивают эти трансляции с помощью динамических трансляций и выполняют соответствующие действия.
Таким образом, AsyncTasks не должен заботиться о конкретном экземпляре Activity, который обрабатывает их результат. Они просто "кричат", когда они закончены, и если в это время активность (активна и сосредоточена/находится в состоянии возобновления), которая заинтересована в результатах задачи, тогда она будет обработана.
Это связано с немного большими издержками, так как среда выполнения должна обрабатывать широковещательную передачу, но я обычно не против. Я думаю, используя LocalBroadcastManager вместо стандартной по умолчанию, скорость немного ускоряется.
Ответ 3
Вот еще один пример AsyncTask, который использует Fragment
для обработки изменений конфигурации времени выполнения (например, когда пользователь поворачивает экран) с помощью setRetainInstance(true)
. Также демонстрируется определенный (постоянно обновляемый) индикатор выполнения.
Пример частично основан на официальных документах Сохранение объекта во время изменения конфигурации.
В этом примере работа, требующая фонового потока, - это простая загрузка изображения из Интернета в пользовательский интерфейс.
Алекс Локвуд, похоже, прав, что, когда дело доходит до обработки изменений конфигурации, использование AsyncTasks с использованием "Сохраненного фрагмента" - лучшая практика. onRetainNonConfigurationInstance()
устаревает в Lint в Android Studio. Официальные документы предупреждают нас, используя android:configChanges
, из "Обработка изменения конфигурации" ,...
Обработка самого изменения конфигурации может затруднить использование альтернативных ресурсов, поскольку система автоматически не применяет их для вас. Этот метод следует рассматривать как последнее средство, когда вы должны избегать перезапуска из-за изменения конфигурации и не рекомендуется для большинства приложений.
Тогда возникает вопрос, следует ли вообще использовать AsyncTask для фонового потока.
Официальная ссылка для AsyncTask предупреждает...
AsyncTasks в идеале следует использовать для коротких операций (максимум за несколько секунд). Если вам нужно поддерживать потоки в течение длительных периодов времени, настоятельно рекомендуется использовать различные API, предоставляемые java.util.concurrent pacakge, таких как Исполнитель, ThreadPoolExecutor и FutureTask.
В качестве альтернативы можно использовать службу, загрузчик (используя CursorLoader или AsyncTaskLoader) или поставщик контента для выполнения асинхронных операций.
Я разбиваю остальную часть сообщения на:
- Процедура; и
- Весь код для вышеуказанной процедуры.
Процедура
-
Начните с базового AsyncTask как внутреннего класса активности (он не обязательно должен быть внутренним классом, но, вероятно, будет удобно). На этом этапе AsyncTask не обрабатывает изменения конфигурации во время выполнения.
public class ThreadsActivity extends ActionBarActivity {
private ImageView mPictureImageView;
private class LoadImageFromNetworkAsyncTask
extends AsyncTask<String, Void, Bitmap> {
@Override
protected Bitmap doInBackground(String... urls) {
return loadImageFromNetwork(urls[0]);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
mPictureImageView.setImageBitmap(bitmap);
}
}
/**
* Requires in AndroidManifext.xml
* <uses-permission android:name="android.permission.INTERNET" />
*/
private Bitmap loadImageFromNetwork(String url) {
Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeStream((InputStream)
new URL(url).getContent());
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_threads);
mPictureImageView =
(ImageView) findViewById(R.id.imageView_picture);
}
public void getPicture(View view) {
new LoadImageFromNetworkAsyncTask()
.execute("http://i.imgur.com/SikTbWe.jpg");
}
}
-
Добавьте вложенный класс RetainedFragment, который расширяет класс Fragement и не имеет собственного пользовательского интерфейса. Добавьте setRetainInstance (true) в событие onCreate этого фрагмента. Предоставьте процедуры для установки и получения ваших данных.
public class ThreadsActivity extends Activity {
private ImageView mPictureImageView;
private RetainedFragment mRetainedFragment = null;
...
public static class RetainedFragment extends Fragment {
private Bitmap mBitmap;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// The key to making data survive
// runtime configuration changes.
setRetainInstance(true);
}
public Bitmap getData() {
return this.mBitmap;
}
public void setData(Bitmap bitmapToRetain) {
this.mBitmap = bitmapToRetain;
}
}
private class LoadImageFromNetworkAsyncTask
extends AsyncTask<String, Integer,Bitmap> {
....
-
В самом внешнем классе Activity onCreate() обрабатывает RetainedFragment: Reference, если он уже существует (в случае перезапуска Activity); создать и добавить его, если он не существует; Затем, если он уже существует, получите данные из RetainedFragment и настройте свой пользовательский интерфейс с этими данными.
public class ThreadsActivity extends Activity {
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_threads);
final String retainedFragmentTag = "RetainedFragmentTag";
mPictureImageView =
(ImageView) findViewById(R.id.imageView_picture);
mLoadingProgressBar =
(ProgressBar) findViewById(R.id.progressBar_loading);
// Find the RetainedFragment on Activity restarts
FragmentManager fm = getFragmentManager();
// The RetainedFragment has no UI so we must
// reference it with a tag.
mRetainedFragment =
(RetainedFragment) fm.findFragmentByTag(retainedFragmentTag);
// if Retained Fragment doesn't exist create and add it.
if (mRetainedFragment == null) {
// Add the fragment
mRetainedFragment = new RetainedFragment();
fm.beginTransaction()
.add(mRetainedFragment, retainedFragmentTag).commit();
// The Retained Fragment exists
} else {
mPictureImageView
.setImageBitmap(mRetainedFragment.getData());
}
}
-
Инициировать AsyncTask из пользовательского интерфейса
public void getPicture(View view) {
new LoadImageFromNetworkAsyncTask().execute(
"http://i.imgur.com/SikTbWe.jpg");
}
-
Добавить и закодировать определенный индикатор выполнения:
- Добавить индикатор выполнения в макет пользовательского интерфейса;
- Получить ссылку на него в Activity oncreate();
- Сделать видимым и невидимым в начале и конце процесса;
- Определите прогресс для отчета в пользовательском интерфейсе в onProgressUpdate.
- Измените параметр 2-го поколения AsyncTask от Void до типа, который может обрабатывать обновления выполнения (например, Integer).
- publishProgress в обычных точках в doInBackground().
Весь код для вышеуказанной процедуры
Макет действий.
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.mysecondapp.ThreadsActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<ImageView
android:id="@+id/imageView_picture"
android:layout_width="300dp"
android:layout_height="300dp"
android:background="@android:color/black" />
<Button
android:id="@+id/button_get_picture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@id/imageView_picture"
android:onClick="getPicture"
android:text="Get Picture" />
<Button
android:id="@+id/button_clear_picture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@id/button_get_picture"
android:layout_toEndOf="@id/button_get_picture"
android:layout_toRightOf="@id/button_get_picture"
android:onClick="clearPicture"
android:text="Clear Picture" />
<ProgressBar
android:id="@+id/progressBar_loading"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/button_get_picture"
android:progress="0"
android:indeterminateOnly="false"
android:visibility="invisible" />
</RelativeLayout>
</ScrollView>
Активность с: подклассом внутреннего класса AsyncTask; subclassed внутренний класс RetainedFragment, который обрабатывает изменения конфигурации во время выполнения (например, когда пользователь поворачивает экран); и определённое обновление строки выполнения с регулярными интервалами....
public class ThreadsActivity extends Activity {
private ImageView mPictureImageView;
private RetainedFragment mRetainedFragment = null;
private ProgressBar mLoadingProgressBar;
public static class RetainedFragment extends Fragment {
private Bitmap mBitmap;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// The key to making data survive runtime configuration changes.
setRetainInstance(true);
}
public Bitmap getData() {
return this.mBitmap;
}
public void setData(Bitmap bitmapToRetain) {
this.mBitmap = bitmapToRetain;
}
}
private class LoadImageFromNetworkAsyncTask extends AsyncTask<String,
Integer, Bitmap> {
@Override
protected Bitmap doInBackground(String... urls) {
// Simulate a burdensome load.
int sleepSeconds = 4;
for (int i = 1; i <= sleepSeconds; i++) {
SystemClock.sleep(1000); // milliseconds
publishProgress(i * 20); // Adjust for a scale to 100
}
return com.example.standardapplibrary.android.Network
.loadImageFromNetwork(
urls[0]);
}
@Override
protected void onProgressUpdate(Integer... progress) {
mLoadingProgressBar.setProgress(progress[0]);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
publishProgress(100);
mRetainedFragment.setData(bitmap);
mPictureImageView.setImageBitmap(bitmap);
mLoadingProgressBar.setVisibility(View.INVISIBLE);
publishProgress(0);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_threads);
final String retainedFragmentTag = "RetainedFragmentTag";
mPictureImageView = (ImageView) findViewById(R.id.imageView_picture);
mLoadingProgressBar = (ProgressBar) findViewById(R.id.progressBar_loading);
// Find the RetainedFragment on Activity restarts
FragmentManager fm = getFragmentManager();
// The RetainedFragment has no UI so we must reference it with a tag.
mRetainedFragment = (RetainedFragment) fm.findFragmentByTag(
retainedFragmentTag);
// if Retained Fragment doesn't exist create and add it.
if (mRetainedFragment == null) {
// Add the fragment
mRetainedFragment = new RetainedFragment();
fm.beginTransaction().add(mRetainedFragment,
retainedFragmentTag).commit();
// The Retained Fragment exists
} else {
mPictureImageView.setImageBitmap(mRetainedFragment.getData());
}
}
public void getPicture(View view) {
mLoadingProgressBar.setVisibility(View.VISIBLE);
new LoadImageFromNetworkAsyncTask().execute(
"http://i.imgur.com/SikTbWe.jpg");
}
public void clearPicture(View view) {
mRetainedFragment.setData(null);
mPictureImageView.setImageBitmap(null);
}
}
В этом примере функция библиотеки (на которую ссылается выше с явным префиксом пакета com.example.standardapplibrary.android.Network), который выполняет реальную работу...
public static Bitmap loadImageFromNetwork(String url) {
Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeStream((InputStream) new URL(url)
.getContent());
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
Добавьте любые разрешения, которые потребуются вашей фоновой задаче для AndroidManifest.xml...
<manifest>
...
<uses-permission android:name="android.permission.INTERNET" />
Добавьте свою активность в AndroidManifest.xml...
<manifest>
...
<application>
<activity
android:name=".ThreadsActivity"
android:label="@string/title_activity_threads"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.example.mysecondapp.MainActivity" />
</activity>
Ответ 4
В последнее время я нашел хорошее решение здесь. Он основан на сохранении объекта задачи через RetainConfiguration. С моей точки зрения, решение очень изящно, и для меня я начал его использовать. Вам нужно просто вставить свою асинтеску из basetask и все.
Ответ 5
На основании ответа @Alex Lockwood и на @William и @quickdraw mcgraw отвечает на этот пост: Как обрабатывать сообщения обработчика, когда действие/фрагмент приостановлено, я написал общее решение.
Таким образом обрабатывается ротация, и если действие переходит к фону во время выполнения асинхронной задачи, действие будет получать обратные вызовы (onPreExecute, onProgressUpdate, onPostExecute и onCancelled) после возобновления, поэтому исключение IllegalStateException не будет выбрано (см. Как обрабатывать сообщения обработчика при приостановке действия/фрагмента).
Было бы здорово иметь то же самое, но с типичными типами аргументов, например AsyncTask (например: AsyncTaskFragment < Params, Progress, Result > ), но мне не удалось это сделать быстро и у вас нет времени на данный момент. Если кто-то хочет сделать улучшение, пожалуйста, не стесняйтесь!
Код:
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Message;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;
public class AsyncTaskFragment extends Fragment {
/* ------------------------------------------------------------------------------------------ */
// region Classes & Interfaces
public static abstract class Task extends AsyncTask<Object, Object, Object> {
private AsyncTaskFragment _fragment;
private void setFragment(AsyncTaskFragment fragment) {
_fragment = fragment;
}
@Override
protected final void onPreExecute() {
// Save the state :
_fragment.setRunning(true);
// Send a message :
sendMessage(ON_PRE_EXECUTE_MESSAGE, null);
}
@Override
protected final void onPostExecute(Object result) {
// Save the state :
_fragment.setRunning(false);
// Send a message :
sendMessage(ON_POST_EXECUTE_MESSAGE, result);
}
@Override
protected final void onProgressUpdate(Object... values) {
// Send a message :
sendMessage(ON_PROGRESS_UPDATE_MESSAGE, values);
}
@Override
protected final void onCancelled() {
// Save the state :
_fragment.setRunning(false);
// Send a message :
sendMessage(ON_CANCELLED_MESSAGE, null);
}
private void sendMessage(int what, Object obj) {
Message message = new Message();
message.what = what;
message.obj = obj;
Bundle data = new Bundle(1);
data.putString(EXTRA_FRAGMENT_TAG, _fragment.getTag());
message.setData(data);
_fragment.handler.sendMessage(message);
}
}
public interface AsyncTaskFragmentListener {
void onPreExecute(String fragmentTag);
void onProgressUpdate(String fragmentTag, Object... progress);
void onCancelled(String fragmentTag);
void onPostExecute(String fragmentTag, Object result);
}
private static class AsyncTaskFragmentPauseHandler extends PauseHandler {
@Override
final protected void processMessage(Activity activity, Message message) {
switch (message.what) {
case ON_PRE_EXECUTE_MESSAGE : { ((AsyncTaskFragmentListener)activity).onPreExecute(message.getData().getString(EXTRA_FRAGMENT_TAG)); break; }
case ON_POST_EXECUTE_MESSAGE : { ((AsyncTaskFragmentListener)activity).onPostExecute(message.getData().getString(EXTRA_FRAGMENT_TAG), message.obj); break; }
case ON_PROGRESS_UPDATE_MESSAGE : { ((AsyncTaskFragmentListener)activity).onProgressUpdate(message.getData().getString(EXTRA_FRAGMENT_TAG), ((Object[])message.obj)); break; }
case ON_CANCELLED_MESSAGE : { ((AsyncTaskFragmentListener)activity).onCancelled(message.getData().getString(EXTRA_FRAGMENT_TAG)); break; }
}
}
}
// endregion
/* ------------------------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------------------------ */
// region Attributes
private Task _task;
private AsyncTaskFragmentListener _listener;
private boolean _running = false;
private static final String EXTRA_FRAGMENT_TAG = "EXTRA_FRAGMENT_TAG";
private static final int ON_PRE_EXECUTE_MESSAGE = 0;
private static final int ON_POST_EXECUTE_MESSAGE = 1;
private static final int ON_PROGRESS_UPDATE_MESSAGE = 2;
private static final int ON_CANCELLED_MESSAGE = 3;
private AsyncTaskFragmentPauseHandler handler = new AsyncTaskFragmentPauseHandler();
// endregion
/* ------------------------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------------------------ */
// region Getters
public AsyncTaskFragmentListener getListener() { return _listener; }
public boolean isRunning() { return _running; }
// endregion
/* ------------------------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------------------------ */
// region Setters
public void setTask(Task task) {
_task = task;
_task.setFragment(this);
}
public void setListener(AsyncTaskFragmentListener listener) { _listener = listener; }
private void setRunning(boolean running) { _running = running; }
// endregion
/* ------------------------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------------------------ */
// region Fragment lifecycle
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
@Override
public void onResume() {
super.onResume();
handler.resume(getActivity());
}
@Override
public void onPause() {
super.onPause();
handler.pause();
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
_listener = (AsyncTaskFragmentListener) activity;
}
@Override
public void onDetach() {
super.onDetach();
_listener = null;
}
// endregion
/* ------------------------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------------------------ */
// region Utils
public void execute(Object... params) {
_task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
}
public void cancel(boolean mayInterruptIfRunning) {
_task.cancel(mayInterruptIfRunning);
}
public static AsyncTaskFragment getRetainedOrNewFragment(AppCompatActivity activity, String fragmentTag) {
FragmentManager fm = activity.getSupportFragmentManager();
AsyncTaskFragment fragment = (AsyncTaskFragment) fm.findFragmentByTag(fragmentTag);
if (fragment == null) {
fragment = new AsyncTaskFragment();
fragment.setListener( (AsyncTaskFragmentListener) activity);
fm.beginTransaction().add(fragment, fragmentTag).commit();
}
return fragment;
}
// endregion
/* ------------------------------------------------------------------------------------------ */
}
Вам понадобится PauseHandler:
import android.app.Activity;
import android.os.Handler;
import android.os.Message;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Message Handler class that supports buffering up of messages when the activity is paused i.e. in the background.
*
* /questions/45707/how-to-handle-handler-messages-when-activityfragment-is-paused
*/
public abstract class PauseHandler extends Handler {
/**
* Message Queue Buffer
*/
private final List<Message> messageQueueBuffer = Collections.synchronizedList(new ArrayList<Message>());
/**
* Flag indicating the pause state
*/
private Activity activity;
/**
* Resume the handler.
*/
public final synchronized void resume(Activity activity) {
this.activity = activity;
while (messageQueueBuffer.size() > 0) {
final Message msg = messageQueueBuffer.get(0);
messageQueueBuffer.remove(0);
sendMessage(msg);
}
}
/**
* Pause the handler.
*/
public final synchronized void pause() {
activity = null;
}
/**
* Store the message if we have been paused, otherwise handle it now.
*
* @param msg Message to handle.
*/
@Override
public final synchronized void handleMessage(Message msg) {
if (activity == null) {
final Message msgCopy = new Message();
msgCopy.copyFrom(msg);
messageQueueBuffer.add(msgCopy);
} else {
processMessage(activity, msg);
}
}
/**
* Notification message to be processed. This will either be directly from
* handleMessage or played back from a saved message when the activity was
* paused.
*
* @param activity Activity owning this Handler that isn't currently paused.
* @param message Message to be handled
*/
protected abstract void processMessage(Activity activity, Message message);
}
Использование образца:
public class TestActivity extends AppCompatActivity implements AsyncTaskFragmentListener {
private final static String ASYNC_TASK_FRAGMENT_A = "ASYNC_TASK_FRAGMENT_A";
private final static String ASYNC_TASK_FRAGMENT_B = "ASYNC_TASK_FRAGMENT_B";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Button testButton = (Button) findViewById(R.id.test_button);
final AsyncTaskFragment fragment = AsyncTaskFragment.getRetainedOrNewFragment(TestActivity.this, ASYNC_TASK_FRAGMENT_A);
testButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(!fragment.isRunning()) {
fragment.setTask(new Task() {
@Override
protected Object doInBackground(Object... objects) {
// Do your async stuff
return null;
}
});
fragment.execute();
}
}
});
}
@Override
public void onPreExecute(String fragmentTag) {}
@Override
public void onProgressUpdate(String fragmentTag, Float percent) {}
@Override
public void onCancelled(String fragmentTag) {}
@Override
public void onPostExecute(String fragmentTag, Object result) {
switch (fragmentTag) {
case ASYNC_TASK_FRAGMENT_A: {
// Handle ASYNC_TASK_FRAGMENT_A
break;
}
case ASYNC_TASK_FRAGMENT_B: {
// Handle ASYNC_TASK_FRAGMENT_B
break;
}
}
}
}
Ответ 6
Для тех, кто хочет уклониться от фрагментов, вы можете сохранить AsyncTask, работающую с изменениями ориентации, используя onRetainCustomNonConfigurationInstance() и некоторые проводки.
(Обратите внимание, что этот метод является альтернативой устаревшему onRetainNonConfigurationInstance()).
Похоже, что это решение часто не упоминается.
Я написал простой пример для иллюстрации.
Ура!
public class MainActivity extends AppCompatActivity {
private TextView result;
private Button run;
private AsyncTaskHolder asyncTaskHolder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
result = (TextView) findViewById(R.id.textView_result);
run = (Button) findViewById(R.id.button_run);
asyncTaskHolder = getAsyncTaskHolder();
run.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
asyncTaskHolder.execute();
}
});
}
private AsyncTaskHolder getAsyncTaskHolder() {
if (this.asyncTaskHolder != null) {
return asyncTaskHolder;
}
//Not deprecated. Get the same instance back.
Object instance = getLastCustomNonConfigurationInstance();
if (instance == null) {
instance = new AsyncTaskHolder();
}
if (!(instance instanceof ActivityDependant)) {
Log.e("", instance.getClass().getName() + " must implement ActivityDependant");
}
return (AsyncTaskHolder) instance;
}
@Override
//Not deprecated. Save the object containing the running task.
public Object onRetainCustomNonConfigurationInstance() {
return asyncTaskHolder;
}
@Override
protected void onStart() {
super.onStart();
if (asyncTaskHolder != null) {
asyncTaskHolder.attach(this);
}
}
@Override
protected void onStop() {
super.onStop();
if (asyncTaskHolder != null) {
asyncTaskHolder.detach();
}
}
void updateUI(String value) {
this.result.setText(value);
}
interface ActivityDependant {
void attach(Activity activity);
void detach();
}
class AsyncTaskHolder implements ActivityDependant {
private Activity parentActivity;
private boolean isRunning;
private boolean isUpdateOnAttach;
@Override
public synchronized void attach(Activity activity) {
this.parentActivity = activity;
if (isUpdateOnAttach) {
((MainActivity) parentActivity).updateUI("done");
isUpdateOnAttach = false;
}
}
@Override
public synchronized void detach() {
this.parentActivity = null;
}
public synchronized void execute() {
if (isRunning) {
Toast.makeText(parentActivity, "Already running", Toast.LENGTH_SHORT).show();
return;
}
isRunning = true;
new AsyncTask<Void, Integer, Void>() {
@Override
protected Void doInBackground(Void... params) {
for (int i = 0; i < 100; i += 10) {
try {
Thread.sleep(500);
publishProgress(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
if (parentActivity != null) {
((MainActivity) parentActivity).updateUI(String.valueOf(values[0]));
}
}
@Override
protected synchronized void onPostExecute(Void aVoid) {
if (parentActivity != null) {
((MainActivity) parentActivity).updateUI("done");
} else {
isUpdateOnAttach = true;
}
isRunning = false;
}
}.execute();
}
}
Ответ 7
Я внедрил library, который может решить проблемы с приостановкой активности и отдыхом во время выполнения вашей задачи.
Вы должны реализовать AsmykPleaseWaitTask
и AsmykBasicPleaseWaitActivity
. Ваша активность и фоновая задача будут работать нормально, даже если вы будете вращать экран и переключаться между приложениями
Ответ 8
Для этого вы можете использовать Loaders. Проверьте Doc здесь
Ответ 9
FAST WORKAROUND (не рекомендуется)
Чтобы избежать действия, чтобы уничтожить и создать себя, нужно объявить свою активность в файле манифеста:
Android: configChanges = "ориентация | keyboardHidden | Размер экрана
<activity
android:name=".ui.activity.MyActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:label="@string/app_name">
Как упоминалось в docs
Изменена ориентация экрана - пользователь повернул устройство.
Примечание. Если ваше приложение нацелено на уровень API 13 или выше (как указано с помощью атрибутов minSdkVersion и targetSdkVersion), тогда вы должны также объявить конфигурацию "screenSize", поскольку она также изменяет когда устройство переключается между портретной и альбомной ориентацией.