Как обрабатывать асинктюс во время вращения экрана?

StackOverflow https://stackoverflow.com/questions/2620917

Вопрос

Я много читаю о том, как сохранить состояние моего экземпляра или как иметь дело с моей деятельностью, разрушенной во время вращения экрана.

Кажется, что есть много возможностей, но я не разобрался, какой из них лучше всего подходит для получения результатов асинктюз.

У меня есть некоторые асинктюды, которые просто начинаются снова и называют isFinishing() Способ деятельности и, если деятельность заканчивается, они не будут обновлять ничего.

Проблема в том, что у меня есть одна задача, которая делает запрос на веб-сервис, которая может потерпеть неудачу или добиться успеха, и перезапустить задачу, приведет к финансовой убытке для пользователя.

Как бы вы решили это? Каковы преимущества или недостатки возможных решений?

Это было полезно?

Решение

Мое первое предложение было бы, чтобы убедиться, что вам действительно нужна ваша деятельность, которая будет сброшена на вращении экрана (поведение по умолчанию). Каждый раз, когда у меня были проблемы с вращением, я добавил этот атрибут моему <activity> Тег в androidmanifest.xml и был просто в порядке.

android:configChanges="keyboardHidden|orientation"

Это выглядит странно, но что он делает это от руки к вашему onConfigurationChanged() Метод, если вы не поставляете его, просто ничего не делает, кроме повторной измерения макета, который, похоже, является совершенно адекватным способом обращения с вращением большую часть времени.

Другие советы

Вы можете проверить, как я обращаюсь AsyncTaskS и ориентация меняется в Code.google.com/p/shelves.. Отказ Есть различные способы сделать это, тот, который я выбрал в этом приложении, - это отменить любую текущую задачу, сохранить его состояние и начать новый с сохраненным состоянием, когда новый Activity создано. Это легко сделать, хорошо работает хорошо, и как бонус, это заботится о том, чтобы остановить ваши задачи, когда пользователь покинет приложение.

Вы также можете использовать onRetainNonConfigurationInstance() пройти ваш AsyncTask к новому Activity (Будьте осторожны о том, чтобы не протекать предыдущий Activity Таким образом, хотя.)

Это самый интересный вопрос, который я видел в отношении Android !!! На самом деле я уже искал решение в течение последних месяцев. До сих пор не решил.

Будьте осторожны, просто переопределить

android:configChanges="keyboardHidden|orientation"

вещи недостаточно.

Рассмотрим случай, когда пользователь получает телефонный звонок, пока ваша ASYNCTASK работает. Ваш запрос уже обрабатывается сервером, поэтому Asynctask ждет ответа. В этот момент ваше приложение идет в фоновом режиме, потому что приложение для телефона только что придет на переднем плане. ОС может убить вашу активность, так как это на заднем плане.

Почему бы вам не всегда хранить ссылку на нынешнюю асинктюзную на синглтон, предоставленном Android?

Всякий раз, когда задача начинается, на преэксекут или на строитель, вы определяете:

((Application) getApplication()).setCurrentTask(asyncTask);

Всякий раз, когда это заканчивается, вы установите его на NULL.

Таким образом, у вас всегда есть ссылка, которая позволяет сделать что-то вроде, OnCreate или Onreume, как присвоено для вашей конкретной логики:

this.asyncTaskReference = ((Application) getApplication()).getCurrentTask();

Если это ноль, вы знаете, что в настоящее время нет бега!

:-)

Наиболее правильным способом этого является использование фрагмента, чтобы сохранить экземпляр ASYNC-задания, по поводу вращений.

Вот ссылка на очень простой пример, что позволяет легко следовать интеграции этой методики в ваши приложения.

https://gist.github.com/daichan4649/2480065.

В Pro android 4. Отказ Автор предложил приятно, что вы должны использовать weak reference.

Слабая справочная нота

На мою точку зрения, лучше хранить асинктюзью через onRetainNonConfigurationInstance Развязывая его из текущего объекта активности и связывающим его на новый объект активности после изменения ориентации. Здесь Я нашел очень хороший пример, как работать с Asynctask и ProgressDialog.

Android: Обработка фона / Async Opeartion с изменением конфигурации

Для поддержания состояний Async Opeartion в фоновом процессе: вы можете получить помощь фрагментов.

Смотрите следующие шаги:

Шаг 1: Создайте неверный фрагмент, пусть справочная задача и добавьте личный класс задач Async.

Шаг 2 (Дополнительный шаг): Если вы хотите поставить курсор загрузки в верхней части вашего действия, используйте ниже код:

Шаг 3: В главной активности реализуйте интерфейс фоназаstaskCallbacks, определенные на шаге 1

class BackgroundTask extends Fragment {
public BackgroundTask() {

}

// Add a static interface 

static interface BackgroundTaskCallbacks {
    void onPreExecute();

    void onCancelled();

    void onPostExecute();

    void doInBackground();
}

private BackgroundTaskCallbacks callbacks;
private PerformAsyncOpeation asyncOperation;
private boolean isRunning;
private final String TAG = BackgroundTask.class.getSimpleName();

/**
 * Start the async operation.
 */
public void start() {
    Log.d(TAG, "********* BACKGROUND TASK START OPERATION ENTER *********");
    if (!isRunning) {
        asyncOperation = new PerformAsyncOpeation();
        asyncOperation.execute();
        isRunning = true;
    }
    Log.d(TAG, "********* BACKGROUND TASK START OPERATION EXIT *********");
}

/**
 * Cancel the background task.
 */
public void cancel() {
    Log.d(TAG, "********* BACKGROUND TASK CANCEL OPERATION ENTER *********");
    if (isRunning) {
        asyncOperation.cancel(false);
        asyncOperation = null;
        isRunning = false;
    }
    Log.d(TAG, "********* BACKGROUND TASK CANCEL OPERATION EXIT *********");
}

/**
 * Returns the current state of the background task.
 */
public boolean isRunning() {
    return isRunning;
}

/**
 * Android passes us a reference to the newly created Activity by calling
 * this method after each configuration change.
 */
public void onAttach(Activity activity) {
    Log.d(TAG, "********* BACKGROUND TASK ON ATTACH ENTER *********");
    super.onAttach(activity);
    if (!(activity instanceof BackgroundTaskCallbacks)) {
        throw new IllegalStateException(
                "Activity must implement the LoginCallbacks interface.");
    }

    // Hold a reference to the parent Activity so we can report back the
    // task's
    // current progress and results.
    callbacks = (BackgroundTaskCallbacks) activity;
    Log.d(TAG, "********* BACKGROUND TASK ON ATTACH EXIT *********");
}

public void onCreate(Bundle savedInstanceState) {
    Log.d(TAG, "********* BACKGROUND TASK ON CREATE ENTER *********");
    super.onCreate(savedInstanceState);
    // Retain this fragment across configuration changes.
    setRetainInstance(true);
    Log.d(TAG, "********* BACKGROUND TASK ON CREATE EXIT *********");
}

public void onDetach() {
    super.onDetach();
    callbacks = null;
}

private class PerformAsyncOpeation extends AsyncTask<Void, Void, Void> {
    protected void onPreExecute() {
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON PRE EXECUTE ENTER *********");
        if (callbacks != null) {
            callbacks.onPreExecute();
        }
        isRunning = true;
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON PRE EXECUTE EXIT *********");
    }

    protected Void doInBackground(Void... params) {
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > DO IN BACKGROUND ENTER *********");
        if (callbacks != null) {
            callbacks.doInBackground();
        }
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > DO IN BACKGROUND EXIT *********");
        return null;
    }

    protected void onCancelled() {
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON CANCEL ENTER *********");
        if (callbacks != null) {
            callbacks.onCancelled();
        }
        isRunning = false;
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON CANCEL EXIT *********");
    }

    protected void onPostExecute(Void ignore) {
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON POST EXECUTE ENTER *********");
        if (callbacks != null) {
            callbacks.onPostExecute();
        }
        isRunning = false;
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON POST EXECUTE EXIT *********");
    }
}

public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    setRetainInstance(true);
}

public void onStart() {
    super.onStart();
}

public void onResume() {
    super.onResume();
}

public void onPause() {
    super.onPause();
}

public void onStop() {
    super.onStop();
}

public class ProgressIndicator extends Dialog {

public ProgressIndicator(Context context, int theme) {
    super(context, theme);
}

private ProgressBar progressBar;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.progress_indicator);
    this.setCancelable(false);
    progressBar = (ProgressBar) findViewById(R.id.progressBar);
    progressBar.getIndeterminateDrawable().setColorFilter(R.color.DarkBlue, android.graphics.PorterDuff.Mode.SCREEN);
}

@Override
public void show() {
    super.show();
}

@Override
public void dismiss() {
    super.dismiss();
}

@Override
public void cancel() {
    super.cancel();
}

public class MyActivity extends FragmentActivity implements BackgroundTaskCallbacks,{

private static final String KEY_CURRENT_PROGRESS = "current_progress";

ProgressIndicator progressIndicator = null;

private final static String TAG = MyActivity.class.getSimpleName();

private BackgroundTask task = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(//"set your layout here");
    initialize your views and widget here .............



    FragmentManager fm = getSupportFragmentManager();
    task = (BackgroundTask) fm.findFragmentByTag("login");

    // If the Fragment is non-null, then it is currently being
    // retained across a configuration change.
    if (task == null) {
        task = new BackgroundTask();
        fm.beginTransaction().add(task, "login").commit();
    }

    // Restore saved state
    if (savedInstanceState != null) {
        Log.i(TAG, "KEY_CURRENT_PROGRESS_VALUE ON CREATE :: "
                + task.isRunning());
        if (task.isRunning()) {
            progressIndicator = new ProgressIndicator(this,
                    R.style.TransparentDialog);
            if (progressIndicator != null) {
                progressIndicator.show();
            }
        }
    }
}

@Override
protected void onPause() {
    // TODO Auto-generated method stub
    super.onPause();

}

@Override
protected void onSaveInstanceState(Bundle outState) {
    // save the current state of your operation here by saying this 

    super.onSaveInstanceState(outState);
    Log.i(TAG, "KEY_CURRENT_PROGRESS_VALUE ON SAVE INSTANCE :: "
            + task.isRunning());
    outState.putBoolean(KEY_CURRENT_PROGRESS, task.isRunning());
    if (progressIndicator != null) {
        progressIndicator.dismiss();
        progressIndicator.cancel();
    }
    progressIndicator = null;
}


private void performOperation() {

            if (!task.isRunning() && progressIndicator == null) {
                progressIndicator = new ProgressIndicator(this,
                        R.style.TransparentDialog);
                progressIndicator.show();
            }
            if (task.isRunning()) {
                task.cancel();
            } else {
                task.start();
            }
        }


@Override
protected void onDestroy() {
    super.onDestroy();
    if (progressIndicator != null) {
        progressIndicator.dismiss();
        progressIndicator.cancel();
    }
    progressIndicator = null;
}

@Override
public void onPreExecute() {
    Log.i(TAG, "CALLING ON PRE EXECUTE");
}

@Override
public void onCancelled() {
    Log.i(TAG, "CALLING ON CANCELLED");
    if (progressIndicator != null) {
        progressIndicator.dismiss();
        progressIndicator.cancel();

}

public void onPostExecute() {
    Log.i(TAG, "CALLING ON POST EXECUTE");
    if (progressIndicator != null) {
        progressIndicator.dismiss();
        progressIndicator.cancel();
        progressIndicator = null;
    }
}

@Override
public void doInBackground() {
    // put your code here for background operation
}

}

Одно следует рассмотреть, является ли результат асинктрики только для деятельности, которая начала задачу. Если да, то Ответ Romain Guy лучший. Если он должен быть доступен для других видов деятельности вашего приложения, то в onPostExecute вы можете использовать LocalBroadcastManager.

LocalBroadcastManager.getInstance(getContext()).sendBroadcast(new Intent("finished"));

Вам также необходимо будет убедиться, что активность правильно обрабатывает ситуацию, когда трансляция отправляется, когда активность приостановлена.

Посмотри на это сообщение. Отказ Этот пост включает в себя Asynctask, выполняющую длительную эксплуатацию и утечку памяти, когда вращение экрана происходит как в одном примере приложения. Применение приложения доступно на исходный кузнец

Мое решение.

В моем случае у меня есть цепочка асинктюзков с тем же контекстом. Деятельность имела доступ только к первому. Чтобы отменить любую бегущую задачу, я сделал следующее:

public final class TaskLoader {

private static AsyncTask task;

     private TaskLoader() {
         throw new UnsupportedOperationException();
     }

     public static void setTask(AsyncTask task) {
         TaskLoader.task = task;
     }

    public static void cancel() {
         TaskLoader.task.cancel(true);
     }
}

Задача doInBackground():

protected Void doInBackground(Params... params) {
    TaskLoader.setTask(this);
    ....
}

Деятельность onStop() или onPause():

protected void onStop() {
    super.onStop();
    TaskLoader.cancel();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    final AddTask task = mAddTask;
    if (task != null && task.getStatus() != UserTask.Status.FINISHED) {
        final String bookId = task.getBookId();
        task.cancel(true);

        if (bookId != null) {
            outState.putBoolean(STATE_ADD_IN_PROGRESS, true);
            outState.putString(STATE_ADD_BOOK, bookId);
        }

        mAddTask = null;
    }
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
    if (savedInstanceState.getBoolean(STATE_ADD_IN_PROGRESS)) {
        final String id = savedInstanceState.getString(STATE_ADD_BOOK);
        if (!BooksManager.bookExists(getContentResolver(), id)) {
            mAddTask = (AddTask) new AddTask().execute(id);
        }
    }
}

Вы также можете добавить Android: configchanges = "Keyboardhidden | Ориентация | Скринзование"

к вашему проявлению примера я надеюсь, что это поможет

 <application
    android:name=".AppController"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:configChanges="keyboardHidden|orientation|screenSize"
    android:theme="@style/AppTheme">
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top