Как обработать изменение ориентации экрана при активном диалоговом окне выполнения и фоновом потоке?

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

Вопрос

Моя программа выполняет некоторую сетевую активность в фоновом потоке.Перед запуском появляется диалоговое окно "Ход выполнения".Диалоговое окно закрывается в обработчике.Все это работает нормально, за исключением случаев, когда ориентация экрана меняется во время работы диалогового окна (и запускается фоновый поток).На этом этапе приложение либо выходит из строя, либо заходит в тупик, либо переходит в странную стадию, когда приложение вообще не работает, пока все потоки не будут уничтожены.

Как я могу корректно обработать изменение ориентации экрана?

Приведенный ниже пример кода примерно соответствует тому, что делает моя реальная программа:

public class MyAct extends Activity implements Runnable {
    public ProgressDialog mProgress;

    // UI has a button that when pressed calls send

    public void send() {
         mProgress = ProgressDialog.show(this, "Please wait", 
                      "Please wait", 
                      true, true);
        Thread thread = new Thread(this);
        thread.start();
    }

    public void run() {
        Thread.sleep(10000);
        Message msg = new Message();
        mHandler.sendMessage(msg);
    }

    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            mProgress.dismiss();
        }
    };
}

Стек:

E/WindowManager(  244): Activity MyAct has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@433b7150 that was originally added here
E/WindowManager(  244): android.view.WindowLeaked: Activity MyAct has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@433b7150 that was originally added here
E/WindowManager(  244):     at android.view.ViewRoot.<init>(ViewRoot.java:178)
E/WindowManager(  244):     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:147)
E/WindowManager(  244):     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:90)
E/WindowManager(  244):     at android.view.Window$LocalWindowManager.addView(Window.java:393)
E/WindowManager(  244):     at android.app.Dialog.show(Dialog.java:212)
E/WindowManager(  244):     at android.app.ProgressDialog.show(ProgressDialog.java:103)
E/WindowManager(  244):     at android.app.ProgressDialog.show(ProgressDialog.java:91)
E/WindowManager(  244):     at MyAct.send(MyAct.java:294)
E/WindowManager(  244):     at MyAct$4.onClick(MyAct.java:174)
E/WindowManager(  244):     at android.view.View.performClick(View.java:2129)
E/WindowManager(  244):     at android.view.View.onTouchEvent(View.java:3543)
E/WindowManager(  244):     at android.widget.TextView.onTouchEvent(TextView.java:4664)
E/WindowManager(  244):     at android.view.View.dispatchTouchEvent(View.java:3198)

Я попытался закрыть диалоговое окно выполнения в onSaveInstanceState, но это просто предотвращает немедленный сбой.Фоновый поток все еще работает, и пользовательский интерфейс находится в частично прорисованном состоянии.Нужно отключить все приложение целиком, прежде чем оно снова начнет работать.

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

Решение

Когда вы переключаете ориентации, Android создаст новый вид. Вы, вероятно, получаете сбои, потому что ваша фоновая тема пытается изменить состояние на старом. (У него также могут возникнуть проблемы, потому что ваш фоновый поток не в потоке пользовательского интерфейса)

Я бы посоветовал сделать это нестабильным и обновить его при изменении ориентации.

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

Редактировать: Инженеры Google не рекомендуют этот подход, как описано Дайан Хакборн (он же Хакбод) в этом Stackoverflow Post. Анкет Проверить этот пост в блоге Чтобы получить больше информации.


Вы должны добавить это в объявление деятельности в манифесте:

android:configChanges="orientation|screenSize"

так что это похоже на

<activity android:label="@string/app_name" 
        android:configChanges="orientation|screenSize|keyboardHidden" 
        android:name=".your.package">

Дело в том, что система разрушает деятельность, когда происходит изменение конфигурации. Видеть Конфигурация.

Таким образом, размещение этого в файле конфигурации позволяет избежать системы уничтожения вашей деятельности. Вместо этого это вызывает onConfigurationChanged(Configuration) метод

Я придумал надежное решение для этих проблем, которое соответствует "Android-образу" вещей.У меня есть все мои длительные операции, использующие шаблон IntentService.

То есть мои действия транслируют намерения, IntentService выполняет работу, сохраняет данные в базе данных и затем транслирует липкий намерения.Липкая часть важна, так что даже если действие было приостановлено в течение времени после того, как пользователь инициировал работу, и пропустило трансляцию в реальном времени от IntentService, мы все равно можем ответить и получить данные от вызывающего действия. ProgressDialogs может довольно хорошо работать с этим шаблоном с onSaveInstanceState().

По сути, вам нужно сохранить флаг о том, что у вас запущено диалоговое окно выполнения в пакете сохраненных экземпляров. Не делайте этого сохраните объект диалога выполнения, потому что это приведет к утечке всего Действия.Чтобы иметь постоянный дескриптор диалога выполнения, я сохраняю его как слабую ссылку в объекте приложения.При изменении ориентации или чем-либо еще, что приводит к приостановке действия (телефонный звонок, переход пользователя домой и т.д.), А затем возобновлению, я закрываю старое диалоговое окно и воссоздаю новое диалоговое окно во вновь созданном действии.

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

Итак, подводя итог, можно сказать, что помещение длительных задач в IntentService в сочетании с разумным использованием onSaveInstanceState() позволяет эффективно отслеживать диалоговые окна и затем восстанавливать события жизненного цикла действия.Соответствующие фрагменты кода операции приведены ниже.Вам также понадобится логика в вашем BroadcastReceiver для надлежащей обработки липких намерений, но это выходит за рамки данной задачи.

public void doSignIn(View view) {
    waiting=true;
    AppClass app=(AppClass) getApplication();
    String logingon=getString(R.string.signon);
    app.Dialog=new WeakReference<ProgressDialog>(ProgressDialog.show(AddAccount.this, "", logingon, true));
    ...
}

@Override
protected void onSaveInstanceState(Bundle saveState) {
    super.onSaveInstanceState(saveState);
    saveState.putBoolean("waiting",waiting);
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if(savedInstanceState!=null) {
        restoreProgress(savedInstanceState);    
    }
    ...
}

private void restoreProgress(Bundle savedInstanceState) {
    waiting=savedInstanceState.getBoolean("waiting");
    if (waiting) {
        AppClass app=(AppClass) getApplication();
        ProgressDialog refresher=(ProgressDialog) app.Dialog.get();
        refresher.dismiss();
        String logingon=getString(R.string.signon);
        app.Dialog=new WeakReference<ProgressDialog>(ProgressDialog.show(AddAccount.this, "", logingon, true));
    }
}

Я встретил ту же проблему. Моя деятельность должна анализировать некоторые данные из URL -адреса, и это медленно. Поэтому я создаю ветку для этого, а затем покажу диалог прогресса. Я позволил потоку опубликовать сообщение обратно в поток пользовательского интерфейса через Handler Когда это закончено. В Handler.handleMessage, Я получаю объект данных (готов сейчас) от потока и заполняю его до пользовательского интерфейса. Так что это очень похоже на ваш пример.

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

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

class MyActivity {

    private MyDataObject mDataObject = null;
    private static MyThread mParserThread = null; // static, or make it singleton

    OnCreate() {
        ...
        Object retained = this.getLastNonConfigurationInstance();
        if(retained != null) {
            // data is already completely obtained before config change
            // by my previous self.
            // no need to create thread or show dialog at all
            mDataObject = (MyDataObject) retained;
            populateUI();
        } else if(mParserThread != null && mParserThread.isAlive()){
            // note: mParserThread is a static member or singleton object.
            // config changed during parsing in previous instance. swap handler
            // then wait for it to finish.
            mParserThread.setHandler(new MyHandler());
        } else {
            // no data and no thread. likely initial run
            // create thread, show dialog
            mParserThread = new MyThread(..., new MyHandler());
            mParserThread.start();
            showDialog(DIALOG_PROGRESS);
        }
    }

    // http://android-developers.blogspot.com/2009/02/faster-screen-orientation-change.html
    public Object onRetainNonConfigurationInstance() {
        // my future self can get this without re-downloading
        // if it's already ready.
        return mDataObject;
    }

    // use Activity.showDialog instead of ProgressDialog.show
    // so the dialog can be automatically managed across config change
    @Override
    protected Dialog onCreateDialog(int id) {
        // show progress dialog here
    }

    // inner class of MyActivity
    private class MyHandler extends Handler {
        public void handleMessage(msg) {
            mDataObject = mParserThread.getDataObject();
            populateUI();
            dismissDialog(DIALOG_PROGRESS);
        }
    }
}

class MyThread extends Thread {
    Handler mHandler;
    MyDataObject mDataObject;

    // constructor with handler param
    public MyHandler(..., Handler h) {
        ...
        mHandler = h;
    }

    public void setHandler(Handler h) { mHandler = h; } // for handler swapping after config change
    public MyDataObject getDataObject() { return mDataObject; } // return data object (completed) to caller

    public void run() {
        mDataObject = new MyDataObject();
        // do the lengthy task to fill mDataObject with data
        lengthyTask(mDataObject);
        // done. notify activity
        mHandler.sendEmptyMessage(0); // tell activity: i'm ready. come pick up the data.
    }
}

Вот что работает для меня. Я не знаю, является ли это «правильным» методом, разработанным Android - они утверждают, что это «управление/воссоздание активности во время ротации экрана» на самом деле облегчает ситуацию, поэтому я думаю, что это не должно быть слишком сложным.

Дайте мне знать, если вы видите проблему в моем коде. Как сказано выше, я действительно не знаю, есть ли какой -либо побочный эффект.

Первоначальная предполагаемая проблема заключалась в том, что код не выживет в изменении ориентации экрана. По -видимому, это было «решено», когда программа обрабатывала ориентацию экрана, вместо того, чтобы позволить структуре пользовательского интерфейса (по призыву Ondestroy)).

Я бы утверждал, что если основная проблема заключается в том, что программа не выживет в Ondestroy (), то принятое решение - это просто обходной путь, который оставляет программу с серьезными другими проблемами и уязвимостью. Помните, что в рамках Android конкретно говорится, что ваша деятельность находится в опасности для уничтожения почти в любое время из -за обстоятельств вне вашего контроля. Следовательно, ваша деятельность должна быть в состоянии выжить в Ondestroy () и последующего OnCreate () по любой причине, а не только изменение ориентации экрана.

Если вы собираетесь принять, что ориентация на экран обработки меняется, чтобы решить проблему ОП, вам необходимо убедиться, что другие причины Ondestroy () не приводят к той же ошибке. Вы можете это сделать? Если нет, я бы спросил, действительно ли «принятый» ответ очень хорошим.

Моим решением было расширить ProgressDialog класс, чтобы получить свой собственный MyProgressDialog.
Я переопредел show() а также dismiss() методы блокировки ориентации, прежде чем показывать Dialog и разблокировать его, когда Dialog уволен. Итак, когда Dialog показан, и ориентация устройства изменяется, ориентация экрана остается до тех пор, пока dismiss() вызывается, затем изменение экрана изменяется в соответствии с датчиками/ориентацией на устройство.

Вот мой код:

public class MyProgressDialog extends ProgressDialog {
private Context mContext;

public MyProgressDialog(Context context) {
    super(context);
    mContext = context;
}

public MyProgressDialog(Context context, int theme) {
    super(context, theme);
    mContext = context;
}

public void show() {
    if (mContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
        ((Activity) mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    else
        ((Activity) mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    super.show();
}

public void dismiss() {
    super.dismiss();
    ((Activity) mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
}

}

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

То, что я сделал, это создал макет, в котором в нем есть Brogressbar.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ProgressBar
    android:id="@+id/progressImage"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    />
</RelativeLayout>

Затем в методе Oncreate сделайте следующее

public void onCreate(Bundle icicle) {
    super.onCreate(icicle);
    setContentView(R.layout.progress);
}

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

Например:

mHandler.post(new Runnable(){

public void run() {
        setContentView(R.layout.my_layout);
    } 
});

Это то, что я сделал, и я обнаружил, что он работает быстрее, чем показывает ProgressDialog, и это менее навязчиво и, по моему мнению, лучше.

Однако, если вы хотите использовать ProgressDialog, то этот ответ не для вас.

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

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

У меня есть активность в логине с вложенным AsyncTask класс называется BackgroundLoginTask.

В моем BackgroundLoginTask Я ничего не делаю из обычного, кроме как добавить нулевую проверку при звонке ProgressDialogУвольнение:

@Override
protected void onPostExecute(Boolean result)
{    
if (pleaseWaitDialog != null)
            pleaseWaitDialog.dismiss();
[...]
}

Это для обработки случая, когда фоновая задача завершается, пока Activity не видно и, следовательно, диалог прогресса уже был отклонен onPause() метод

Далее, у моего родителя Activity класс, я создаю глобальные статические ручки для своих AsyncTask класс и мой ProgressDialog ( AsyncTask, будучи вложенным, может получить доступ к этим переменным):

private static BackgroundLoginTask backgroundLoginTask;
private static ProgressDialog pleaseWaitDialog;

Это служит двум целям: во -первых, это позволяет моему Activity всегда получить доступ к AsyncTask объект даже из новой, после ротированной деятельности. Во -вторых, это позволяет моему BackgroundLoginTask Чтобы получить доступ и отклонить ProgressDialog Даже после вращения.

Далее я добавляю это в onPause(), вызывая диалоговое окно прогресса исчезнуть, когда наш Activity покидает передний план (предотвращая эту уродливую «силу закрыть» аварию):

    if (pleaseWaitDialog != null)
    pleaseWaitDialog.dismiss();

Наконец, у меня есть следующее в моем onResume() Метод:

if ((backgroundLoginTask != null) && (backgroundLoginTask.getStatus() == Status.RUNNING))
        {
           if (pleaseWaitDialog != null)
             pleaseWaitDialog.show();
        }

Это позволяет Dialog Повторить после Activity воссоздан.

Вот весь класс:

public class NSFkioskLoginActivity extends NSFkioskBaseActivity {
    private static BackgroundLoginTask backgroundLoginTask;
    private static ProgressDialog pleaseWaitDialog;
    private Controller cont;

    // This is the app entry point.
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (CredentialsAvailableAndValidated())
        {
        //Go to main menu and don't run rest of onCreate method.
            gotoMainMenu();
            return;
        }
        setContentView(R.layout.login);
        populateStoredCredentials();   
    }

    //Save current progress to options when app is leaving foreground
    @Override
    public void onPause()
    {
        super.onPause();
        saveCredentialsToPreferences(false);
        //Get rid of progress dialog in the event of a screen rotation. Prevents a crash.
        if (pleaseWaitDialog != null)
        pleaseWaitDialog.dismiss();
    }

    @Override
    public void onResume()
    {
        super.onResume();
        if ((backgroundLoginTask != null) && (backgroundLoginTask.getStatus() == Status.RUNNING))
        {
           if (pleaseWaitDialog != null)
             pleaseWaitDialog.show();
        }
    }

    /**
     * Go to main menu, finishing this activity
     */
    private void gotoMainMenu()
    {
        startActivity(new Intent(getApplicationContext(), NSFkioskMainMenuActivity.class));
        finish();
    }

    /**
     * 
     * @param setValidatedBooleanTrue If set true, method will set CREDS_HAVE_BEEN_VALIDATED to true in addition to saving username/password.
     */
    private void saveCredentialsToPreferences(boolean setValidatedBooleanTrue)
    {
        SharedPreferences settings = getSharedPreferences(APP_PREFERENCES, MODE_PRIVATE);
        SharedPreferences.Editor prefEditor = settings.edit();
        EditText usernameText = (EditText) findViewById(R.id.editTextUsername);
        EditText pswText = (EditText) findViewById(R.id.editTextPassword);
        prefEditor.putString(USERNAME, usernameText.getText().toString());
        prefEditor.putString(PASSWORD, pswText.getText().toString());
        if (setValidatedBooleanTrue)
        prefEditor.putBoolean(CREDS_HAVE_BEEN_VALIDATED, true);
        prefEditor.commit();
    }

    /**
     * Checks if user is already signed in
     */
    private boolean CredentialsAvailableAndValidated() {
        SharedPreferences settings = getSharedPreferences(APP_PREFERENCES,
                MODE_PRIVATE);
        if (settings.contains(USERNAME) && settings.contains(PASSWORD) && settings.getBoolean(CREDS_HAVE_BEEN_VALIDATED, false) == true)
         return true;   
        else
        return false;
    }

    //Populate stored credentials, if any available
    private void populateStoredCredentials()
    {
        SharedPreferences settings = getSharedPreferences(APP_PREFERENCES,
            MODE_PRIVATE);
        settings.getString(USERNAME, "");
       EditText usernameText = (EditText) findViewById(R.id.editTextUsername);
       usernameText.setText(settings.getString(USERNAME, ""));
       EditText pswText = (EditText) findViewById(R.id.editTextPassword);
       pswText.setText(settings.getString(PASSWORD, ""));
    }

    /**
     * Validate credentials in a seperate thread, displaying a progress circle in the meantime
     * If successful, save credentials in preferences and proceed to main menu activity
     * If not, display an error message
     */
    public void loginButtonClick(View view)
    {
        if (phoneIsOnline())
        {
        EditText usernameText = (EditText) findViewById(R.id.editTextUsername);
        EditText pswText = (EditText) findViewById(R.id.editTextPassword);
           //Call background task worker with username and password params
           backgroundLoginTask = new BackgroundLoginTask();
           backgroundLoginTask.execute(usernameText.getText().toString(), pswText.getText().toString());
        }
        else
        {
        //Display toast informing of no internet access
        String notOnlineMessage = getResources().getString(R.string.noNetworkAccessAvailable);
        Toast toast = Toast.makeText(getApplicationContext(), notOnlineMessage, Toast.LENGTH_SHORT);
        toast.show();
        }
    }

    /**
     * 
     * Takes two params: username and password
     *
     */
    public class BackgroundLoginTask extends AsyncTask<Object, String, Boolean>
    {       
       private Exception e = null;

       @Override
       protected void onPreExecute()
       {
           cont = Controller.getInstance();
           //Show progress dialog
           String pleaseWait = getResources().getString(R.string.pleaseWait);
           String commWithServer = getResources().getString(R.string.communicatingWithServer);
            if (pleaseWaitDialog == null)
              pleaseWaitDialog= ProgressDialog.show(NSFkioskLoginActivity.this, pleaseWait, commWithServer, true);

       }

        @Override
        protected Boolean doInBackground(Object... params)
        {
        try {
            //Returns true if credentials were valid. False if not. Exception if server could not be reached.
            return cont.validateCredentials((String)params[0], (String)params[1]);
        } catch (Exception e) {
            this.e=e;
            return false;
        }
        }

        /**
         * result is passed from doInBackground. Indicates whether credentials were validated.
         */
        @Override
        protected void onPostExecute(Boolean result)
        {
        //Hide progress dialog and handle exceptions
        //Progress dialog may be null if rotation has been switched
        if (pleaseWaitDialog != null)
             {
            pleaseWaitDialog.dismiss();
                pleaseWaitDialog = null;
             }

        if (e != null)
        {
         //Show toast with exception text
                String networkError = getResources().getString(R.string.serverErrorException);
                Toast toast = Toast.makeText(getApplicationContext(), networkError, Toast.LENGTH_SHORT);
            toast.show();
        }
        else
        {
            if (result == true)
            {
            saveCredentialsToPreferences(true);
            gotoMainMenu();
            }
            else
            {
            String toastText = getResources().getString(R.string.invalidCredentialsEntered);
                Toast toast = Toast.makeText(getApplicationContext(), toastText, Toast.LENGTH_SHORT);
            toast.show();
            } 
        }
        }

    }
}

Я ни в коем случае не опытный разработчик Android, так что не стесняйтесь комментировать.

Переместите длинную задачу в отдельный класс. Реализуйте его как паттерн субъекта. Всякий раз, когда зарегистрируется деятельность, зарегистрируется и при закрытии нерегистрации с классом задач. Класс заданий может использовать Asynctask.

Хитрость заключается в том, чтобы показать/отклонить диалог в Asynctask во время OnPreexecute/OnPostexecute, как обычно, хотя в случае с изменениями ориентации создание/показать новый экземпляр диалога в действии и передавать его ссылку на задачу.

public class MainActivity extends Activity {
    private Button mButton;
    private MyTask mTask = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        MyTask task = (MyTask) getLastNonConfigurationInstance();
        if(task != null){
            mTask = task;
            mTask.mContext = this;
            mTask.mDialog = ProgressDialog.show(this, "", "", true);        
        }

        mButton = (Button) findViewById(R.id.button1);
        mButton.setOnClickListener(new View.OnClickListener(){
            public void onClick(View v){
                mTask = new MyTask(MainActivity.this);
                mTask.execute();
            }
        });
    }


    @Override
    public Object onRetainNonConfigurationInstance() {
        String str = "null";
        if(mTask != null){
            str = mTask.toString();
            mTask.mDialog.dismiss();
        }
        Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
        return mTask;
    }



    private class MyTask extends AsyncTask<Void, Void, Void>{
        private ProgressDialog mDialog;
        private MainActivity mContext;


        public MyTask(MainActivity context){
            super();
            mContext = context;
        }


        protected void onPreExecute() {
            mDialog = ProgressDialog.show(MainActivity.this, "", "", true);
        }

        protected void onPostExecute(Void result) {
            mContext.mTask = null;
            mDialog.dismiss();
        }


        @Override
        protected Void doInBackground(Void... params) {
            SystemClock.sleep(5000);
            return null;
        }       
    }
}

Я сделал это так:

    package com.palewar;
    import android.app.Activity;
    import android.app.ProgressDialog;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;

    public class ThreadActivity extends Activity {


        static ProgressDialog dialog;
        private Thread downloadThread;
        final static Handler handler = new Handler() {

            @Override
            public void handleMessage(Message msg) {

                super.handleMessage(msg);

                dialog.dismiss();

            }

        };

        protected void onDestroy() {
    super.onDestroy();
            if (dialog != null && dialog.isShowing()) {
                dialog.dismiss();
                dialog = null;
            }

        }

        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);

            downloadThread = (Thread) getLastNonConfigurationInstance();
            if (downloadThread != null && downloadThread.isAlive()) {
                dialog = ProgressDialog.show(ThreadActivity.this, "",
                        "Signing in...", false);
            }

            dialog = ProgressDialog.show(ThreadActivity.this, "",
                    "Signing in ...", false);

            downloadThread = new MyThread();
            downloadThread.start();
            // processThread();
        }

        // Save the thread
        @Override
        public Object onRetainNonConfigurationInstance() {
            return downloadThread;
        }


        static public class MyThread extends Thread {
            @Override
            public void run() {

                try {
                    // Simulate a slow network
                    try {
                        new Thread().sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    handler.sendEmptyMessage(0);

                } finally {

                }
            }
        }

    }

Вы также можете попытаться сообщить мне, что это работает для вас или нет

Если вы создаете фон Service Это делает все тяжелые подъемы (запросы/ответ TCP, Unmarshalling), View а также Activity может быть уничтожен и воссоздан без протекания окна и не теряя данные. Это позволяет рекомендовать Android поведение, которое должно Уничтожить деятельность при каждом изменении конфигурации (например, для каждого изменения ориентации).

Это немного сложнее, но это лучший способ для вызова запроса сервера, предварительной/постобработки данных и т. Д.

Вы можете даже использовать свой Service Чтобы стоять в очереди на каждый запрос на сервер, так и эффективно обрабатывать эти вещи.

Гид DEV имеет полный Глава Services.

У меня есть реализация, которая позволяет уничтожить деятельность при изменении ориентации экрана, но все же успешно разрушает диалог в воссозданной деятельности. я использую ...NonConfigurationInstance Подключить фоновую задачу к воссозданной деятельности. Обычная каркас Android обрабатывает воссоздание самого диалога, там ничего не изменилось.

Я подкладывал Asynctask, добавив поле для «владения» и методом обновления этого владельца.

class MyBackgroundTask extends AsyncTask<...> {
  MyBackgroundTask (Activity a, ...) {
    super();
    this.ownerActivity = a;
  }

  public void attach(Activity a) {
    ownerActivity = a;
  }

  protected void onPostExecute(Integer result) {
    super.onPostExecute(result);
    ownerActivity.dismissDialog(DIALOG_PROGRESS);
  }

  ...
}

В классе деятельности я добавил поле backgroundTask Ссылаясь на «собственный» фоновой обработке, и я обновляю это поле, используя onRetainNonConfigurationInstance а также getLastNonConfigurationInstance.

class MyActivity extends Activity {
  public void onCreate(Bundle savedInstanceState) {
    ...
    if (getLastNonConfigurationInstance() != null) {
      backgroundTask = (MyBackgroundTask) getLastNonConfigurationInstance();
      backgroundTask.attach(this);
    }
  }

  void startBackgroundTask() {
    backgroundTask = new MyBackgroundTask(this, ...);
    showDialog(DIALOG_PROGRESS);
    backgroundTask.execute(...);
  }

  public Object onRetainNonConfigurationInstance() {
    if (backgroundTask != null && backgroundTask.getStatus() != Status.FINISHED)
      return backgroundTask;
    return null;
  }
  ...
}

Предложения для дальнейшего улучшения:

  • Очистить backgroundTask Ссылка на действие после того, как задача завершена, чтобы выпустить любую память или другие связанные с ней ресурсы.
  • Очистить ownerActivity Ссылка в фоновой обработке до того, как деятельность будет разрушена в случае, если оно не будет воссоздан немедленно.
  • Создать BackgroundTask Интерфейс и/или коллекция, чтобы позволить различным типам задач выполняться от одной и той же владения деятельностью.

Если вы поддерживаете два макета, все потоки пользовательского интерфейса должны быть прекращены.

Если вы используете Asyntask, то вы можете легко позвонить .cancel() Метод внутри onDestroy() Метод текущей деятельности.

@Override
protected void onDestroy (){
    removeDialog(DIALOG_LOGIN_ID); // remove loading dialog
    if (loginTask != null){
        if (loginTask.getStatus() != AsyncTask.Status.FINISHED)
            loginTask.cancel(true); //cancel AsyncTask
    }
    super.onDestroy();
}

Для Asynctask читайте больше в разделе «Отмена задачи» в здесь.

Обновлять:Добавлено условие, чтобы проверить статус, так как оно может быть отменено только в том случае, если оно находится в состоянии управления. Также обратите внимание, что Asynctask может быть выполнена только один раз.

Пытался реализовать jfelectronрешение, потому что это "Рок-решет«Но потребовалось некоторое время, чтобы посмотреть и собрать все упомянутые элементы. В итоге это было немного другим, и я думаю, что более элегантное решение, размещенное здесь во всем.

Использует намеренный сервис, выпущенный из деятельности, чтобы выполнить долгую задачу в отдельном потоке. Сервис отпускает липкие вещание в действие, которое обновляет диалоговое окно. В данной деятельности используется ShowDialog (), Oncreatedialog () и OnPreparedialog (), чтобы устранить необходимость иметь постоянные данные, передаваемые в объекте приложения или пакете SaveDinStanceState. Это должно работать, независимо от того, как ваше приложение прерывается.

Класс деятельности:

public class TesterActivity extends Activity {
private ProgressDialog mProgressDialog;
private static final int PROGRESS_DIALOG = 0;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    Button b = (Button) this.findViewById(R.id.test_button);
    b.setOnClickListener(new OnClickListener() {
        public void onClick(View v) {
            buttonClick();
        }
    });
}

private void buttonClick(){
    clearPriorBroadcast();
    showDialog(PROGRESS_DIALOG);
    Intent svc = new Intent(this, MyService.class);
    startService(svc);
}

protected Dialog onCreateDialog(int id) {
    switch(id) {
    case PROGRESS_DIALOG:
        mProgressDialog = new ProgressDialog(TesterActivity.this);
        mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        mProgressDialog.setMax(MyService.MAX_COUNTER);
        mProgressDialog.setMessage("Processing...");
        return mProgressDialog;
    default:
        return null;
    }
}

@Override
protected void onPrepareDialog(int id, Dialog dialog) {
    switch(id) {
    case PROGRESS_DIALOG:
        // setup a broadcast receiver to receive update events from the long running process
        IntentFilter filter = new IntentFilter();
        filter.addAction(MyService.BG_PROCESS_INTENT);
        registerReceiver(new MyBroadcastReceiver(), filter);
        break;
    }
}

public class MyBroadcastReceiver extends BroadcastReceiver{
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.hasExtra(MyService.KEY_COUNTER)){
            int count = intent.getIntExtra(MyService.KEY_COUNTER, 0);
            mProgressDialog.setProgress(count);
            if (count >= MyService.MAX_COUNTER){
                dismissDialog(PROGRESS_DIALOG);
            }
        }
    }
}

/*
 * Sticky broadcasts persist and any prior broadcast will trigger in the 
 * broadcast receiver as soon as it is registered.
 * To clear any prior broadcast this code sends a blank broadcast to clear 
 * the last sticky broadcast.
 * This broadcast has no extras it will be ignored in the broadcast receiver 
 * setup in onPrepareDialog()
 */
private void clearPriorBroadcast(){
    Intent broadcastIntent = new Intent();
    broadcastIntent.setAction(MyService.BG_PROCESS_INTENT);
    sendStickyBroadcast(broadcastIntent);
}}

Класс intentservice:

public class MyService extends IntentService {

public static final String BG_PROCESS_INTENT = "com.mindspiker.Tester.MyService.TEST";
public static final String KEY_COUNTER = "counter";
public static final int MAX_COUNTER = 100;

public MyService() {
  super("");
}

@Override
protected void onHandleIntent(Intent intent) {
    for (int i = 0; i <= MAX_COUNTER; i++) {
        Log.e("Service Example", " " + i);
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Intent broadcastIntent = new Intent();
        broadcastIntent.setAction(BG_PROCESS_INTENT);
        broadcastIntent.putExtra(KEY_COUNTER, i);
        sendStickyBroadcast(broadcastIntent);
    }
}}

Манифестные записи файла:

Перед разделом заявления:

uses-permission android:name="com.mindspiker.Tester.MyService.TEST"
uses-permission android:name="android.permission.BROADCAST_STICKY"

Внутреннее раздел применения

service android:name=".MyService"

Это мое предложенное решение:

  • Переместить асинктаску или нить в сохраняемый фрагмент, как объяснено здесь. Анкет Я считаю, что это хорошая практика - перемещать все сетевые вызовы в фрагменты. Если вы уже используете фрагменты, один из них может быть ответственен за звонки. В противном случае вы можете создать фрагмент только для выполнения запроса, как предлагается связанная статья.
  • Фрагмент будет использовать интерфейс слушателя, чтобы сигнализировать о выполнении/сбое задачи. Вам не нужно беспокоиться о изменениях ориентации там. Фрагмент всегда будет иметь правильную связь с текущим диалогом активности и прогресса, которую можно безопасно возобновить.
  • Сделайте свой диалог прогресса членом вашего класса. На самом деле вы должны сделать это для всех диалогов. В методе OnPause вы должны отказаться от них, в противном случае вы пропустите окно при изменении конфигурации. Занятое состояние должно быть сохранено фрагментом. Когда фрагмент прикреплен к деятельности, вы можете снова поднять диалог прогресса, если вызов все еще работает. А void showProgressDialog() Метод может быть добавлен в интерфейс слушателя фрагментной активности для этой цели.

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

Во -первых, я создал класс Dialogsingleton, чтобы получить только один экземпляр (Singleton Pattern)

public class DialogSingleton
{
    private static Dialog dialog;

    private static final Object mLock = new Object();
    private static DialogSingleton instance;

    private DialogSingleton()
    {

    }

    public static DialogSingleton GetInstance()
    {
        synchronized (mLock)
        {
            if(instance == null)
            {
                instance = new DialogSingleton();
            }

            return instance;
        }
    }

    public void DialogShow(Context context, String title)
    {
        if(!((Activity)context).isFinishing())
        {
            dialog = new ProgressDialog(context, 2);

            dialog.setCanceledOnTouchOutside(false);

            dialog.setTitle(title);

            dialog.show();
        }
    }

    public void DialogDismiss(Context context)
    {
        if(!((Activity)context).isFinishing() && dialog.isShowing())
        {
            dialog.dismiss();
        }
    }
}

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

DialogSingleton.GetInstance().DialogShow(this, "My title here!");

Когда я закончу с фоновой задачей, я снова называю уникальный экземпляр и отклоняю его диалог.

DialogSingleton.GetInstance().DialogDismiss(this);

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

if(Boolean.parseBoolean(preference.GetValue(IS_TASK_NAME_EXECUTED_KEY, "boolean").toString()))
{
    DialogSingleton.GetInstance().DialogShow(this, "Checking credentials!");
} // preference object gets the info from shared preferences (my own implementation to get and put data to shared preferences) and IS_TASK_NAME_EXECUTED_KEY is the key to save this flag (flag to know if this activity has a background task already running).

Когда я начну запускать фоновую задачу:

preference.AddValue(IS_TASK_NAME_EXECUTED_KEY, true, "boolean");

DialogSingleton.GetInstance().DialogShow(this, "My title here!");

Когда я закончу выполнять фоновую задачу:

preference.AddValue(IS_TASK_NAME_EXECUTED_KEY, false, "boolean");

DialogSingleton.GetInstance().DialogDismiss(ActivityName.this);

Я надеюсь, что это помогает.

Это очень старый вопрос, который по какой -то причине возник на боковой панели.

Если фоновая задача должна только выжить, пока активность находится на переднем плане, «новое» решение состоит в том, чтобы разместить фоновый поток (или, предпочтительно, AsyncTask) в сохранил фрагмент, как описано в этом Руководство разработчика а также Многочисленные вопросы и после.

Оставленный фрагмент выживает, если деятельность разрушена для изменения конфигурации, но нет Когда деятельность разрушена в фоновом или заднем стеке. Следовательно, фоновая задача все еще должна быть прервана, если isChangingConfigurations() ложный в onPause().

Я более свежее в Android, и я попробовал это, и это сработало.

public class loadTotalMemberByBranch extends AsyncTask<Void, Void,Void> {
        ProgressDialog progressDialog = new ProgressDialog(Login.this);
        int ranSucess=0;
        @Override
        protected void onPreExecute() {
            // TODO Auto-generated method stub
            super.onPreExecute();
            progressDialog.setTitle("");    
            progressDialog.isIndeterminate();
            progressDialog.setCancelable(false);
            progressDialog.show();
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);

        }
        @Override
        protected Void doInBackground(Void... params) {
            // TODO Auto-generated method stub

            return null;
        }
        @Override
        protected void onPostExecute(Void result) {
            // TODO Auto-generated method stub
            super.onPostExecute(result);
            progressDialog.dismiss();
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
        }
}

Я все пробовал. Провели дни, экспериментируя. Я не хотел блокировать деятельность от вращения. Мой сценарий был:

  1. Диалог прогресса, показывающий динамическую информацию для пользователя. Например: «Подключение к серверу ...», «Загрузка данных ...» и т. Д.
  2. Поток делает тяжелые вещи и обновляет диалог
  3. Обновление пользовательского интерфейса с результатами в конце.

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

Единственным решением, которое сработало для меня, был трюк активности/диалога. Это просто и гениально, и все это доказательство вращения:

  1. Вместо того, чтобы создать диалог и попросить показать его, создайте действие, которое было установлено в Manifest с помощью Android: Theme = "@Android: Style/Theme.dialog". Итак, это просто похоже на диалог.

  2. Заменить Showdialog (dialog_id) на startActivityForResult (yourActivityDialog, yourcode);

  3. Используйте OnActivityResult в вызовной деятельности, чтобы получить результаты из выполняемого потока (даже ошибок) и обновить пользовательский интерфейс.

  4. На вашем «ActivityDialog» используйте потоки или Asynctask для выполнения длинных задач и onretainnonConfigurationInstance, чтобы сохранить состояние «диалога» при вращении экрана.

Это быстро и работает нормально. Я все еще использую диалоги для других задач и Asynctask для чего -то, что не требует постоянного диалога на экране. Но с этим сценарием я всегда иду за рисунком активности/диалога.

И я не пробовал, но даже можно заблокировать эту деятельность/диалог от вращения, когда поток работает, ускоряя ситуацию, позволяя вращать вызывающую деятельность.

В наши дни существует гораздо более четкий способ решения этих типов проблем. Типичный подход:

1. Убедитесь, что ваши данные должным образом отделены от пользовательского интерфейса:

Все, что является фоновым процессом, должно быть в сохранении Fragment (Установите это с Fragment.setRetainInstance(). Анкет Это становится вашим «настойчивым хранением данных», где сохраняются все, что вы хотели бы сохранить. После события изменения ориентации это Fragment все еще будет доступен в своем первоначальном состоянии через FragmentManager.findFragmentByTag() Позвоните (когда вы его создаете, вы должны дать ему тег не удостоверение личности так как это не прикреплено к View).

Увидеть Обработка изменений времени выполнения Разработанное руководство для получения информации о том, чтобы сделать это правильно и почему это лучший вариант.

2. Убедитесь, что вы правильно и безопасно взаимодействуете между фоновыми процессами и пользовательским интерфейсом:

Вы должны задний ход Ваш процесс связывания. На данный момент ваш фоновый процесс присоединяется к View - Вместо этого ваш View должно быть прикреплено к фоновому процессу. Это имеет смысл правильно? А ViewДействие зависит от фонового процесса, тогда как фоновый процесс не зависит от View. Это означает изменение ссылки на стандарт Listener интерфейс. Скажите свой процесс (какой бы класс он ни был - будь то AsyncTask, Runnable или что -то еще) определяет OnProcessFinishedListener, когда процесс будет сделан, он должен называть этого слушателя, если он существует.

Этот отвечать Хорошее краткое описание того, как сделать индивидуальные слушатели.

3. Свяжите свой пользовательский интерфейс с процессом данных всякий раз, когда создается пользовательский интерфейс (включая изменения ориентации):

Теперь вы должны беспокоиться о том, чтобы взаимодействовать фоновую задачу с тем, что ваш текущий View Структура. Если вы справляетесь с изменениями ориентации правильно (не configChanges Взлом людей всегда рекомендуют), тогда ваш Dialog будет воссоздан системой. Это важно, это означает, что при изменении ориентации все ваши DialogМетоды жизненного цикла напоминают. Итак, в любом из этих методов (onCreateDialog обычно хорошее место), вы можете сделать звонок, как следующее:

DataFragment f = getActivity().getFragmentManager().findFragmentByTag("BACKGROUND_TAG");
if (f != null) {
    f.mBackgroundProcess.setOnProcessFinishedListener(new OnProcessFinishedListener() {
        public void onProcessFinished() {
            dismiss();
        }
    });
 }

Увидеть Жизненный цикл фрагмента Для принятия решения, где настройка слушателя лучше всего подходит для вашей индивидуальной реализации.

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

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

 public class DashListFragment extends Fragment {
     private static DashListFragment ACTIVE_INSTANCE;

     @Override
     public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ACTIVE_INSTANCE = this;

        new Handler().postDelayed(new Runnable() {
            public void run() {
                try {
                        if (ACTIVE_INSTANCE != null) {
                            setAdapter(); // this method do something on ui or use context
                        }
                }
                catch (Exception e) {}


            }
        }, 1500l);

    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        ACTIVE_INSTANCE = null;
    }


}

Если вы боретесь с обнаружением событий изменения ориентации диалога Независимо от ссылки на деятельность, этот метод работает захватывающе хорошо. Я использую это, потому что у меня есть свой собственный класс диалога, который можно показать в разных действиях, поэтому я не всегда знаю, в какой деятельности он отображается. С помощью этого метода вам не нужно менять AndroidManifest, беспокойство о ссылках на деятельность, И вам не нужен собственный диалог (как у меня есть). Однако вам нужен пользовательский представление содержимого, чтобы вы могли обнаружить изменения ориентации, используя этот конкретный представление. Вот мой пример:

Настраивать

public class MyContentView extends View{
    public MyContentView(Context context){
        super(context);
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig){
        super.onConfigurationChanged(newConfig);

        //DO SOMETHING HERE!! :D
    }
}

Реализация 1 - диалог

Dialog dialog = new Dialog(context);
//set up dialog
dialog.setContentView(new MyContentView(context));
dialog.show();

Реализация 2 - AlertDialog.Builder

AlertDialog.Builder builder = new AlertDialog.Builder(context);
//set up dialog builder
builder.setView(new MyContentView(context));        //Can use this method
builder.setCustomTitle(new MycontentView(context)); // or this method
builder.build().show();

Реализация 3 - ProgressDialog / AlertDialog

ProgressDialog progress = new ProgressDialog(context);
//set up progress dialog
progress.setView(new MyContentView(context));        //Can use this method
progress.setCustomTitle(new MyContentView(context)); // or this method
progress.show();

Это мое решение, когда я столкнулся с ним:ProgressDialog это не Fragment Ребенок, так что мой собственный класс "ProgressDialogFragment"Может расширить DialogFragment Вместо этого, чтобы сохранить диалог, показанный для изменений конфигурации.

import androidx.annotation.NonNull;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.os.Bundle; 
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager;

 /**
 * Usage:
 * To display the dialog:
 *     >>> ProgressDialogFragment.showProgressDialogFragment(
 *              getSupportFragmentManager(), 
 *              "fragment_tag", 
 *              "my dialog title", 
 *              "my dialog message");
 *              
 * To hide the dialog
 *     >>> ProgressDialogFragment.hideProgressDialogFragment();
 */ 


public class ProgressDialogFragment extends DialogFragment {

    private static String sTitle, sMessage;
    private static ProgressDialogFragment sProgressDialogFragment;

    public ProgressDialogFragment() {
    }

    private ProgressDialogFragment(String title, String message) {
        sTitle = title;
        sMessage = message;
    }


    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return ProgressDialog.show(getActivity(), sTitle, sMessage);
    }

    public static void showProgressDialogFragment(FragmentManager fragmentManager, String fragmentTag, String title, String message) {
        if (sProgressDialogFragment == null) {
            sProgressDialogFragment = new ProgressDialogFragment(title, message);
            sProgressDialogFragment.show(fragmentManager, fragmentTag);

        } else { // case of config change (device rotation)
            sProgressDialogFragment = (ProgressDialogFragment) fragmentManager.findFragmentByTag(fragmentTag); // sProgressDialogFragment will try to survive its state on configuration as much as it can, but when calling .dismiss() it returns NPE, so we have to reset it on each config change
            sTitle = title;
            sMessage = message;
        }

    }

    public static void hideProgressDialogFragment() {
        if (sProgressDialogFragment != null) {
            sProgressDialogFragment.dismiss();
        }
    }
}

Задача состояла в том, чтобы сохранить заголовок и сообщение диалога во время вращения экрана, когда они сбрасываются в пустую строку по умолчанию, хотя диалог все еще показан

Есть 2 подхода, чтобы решить это:

Первый подход: Сделайте действие, которое использует диалоговое окно для удержания состояния во время изменения конфигурации в манифестном файле:

android:configChanges="orientation|screenSize|keyboardHidden"

Этот подход не является предпочтительным Google.

Второй подход:На деятельности onCreate() метод, вам нужно сохранить свой DialogFragment перестроив ProgressDialogFragment снова с заголовком и сообщением следующим образом, если savedInstanceState не является нулевым:

@Override
protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_deal);

 if (savedInstanceState != null) {
      ProgressDialogFragment saveProgressDialog = (ProgressDialogFragment) getSupportFragmentManager()
              .findFragmentByTag("fragment_tag");
      if (saveProgressDialog != null) {
          showProgressDialogFragment(getSupportFragmentManager(), "fragment_tag", "my dialog title", "my dialog message");
      }
  }
}

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

В рамках метода OnPostexecute моего Asynctask я просто завернул «.dismiss» для диалога прогресса в блоке Try/Catch (с пустым уловом), а затем просто проигнорировал исключение, которое было поднято. Кажется неправильным, но кажется, что нет никаких вредных последствий (по крайней мере, для того, что я делаю впоследствии, который должен начать еще одну деятельность, проходящую в результате моего долгосрочного запроса в качестве дополнительного)

Самое простое и гибкое решение - использовать Asynctask со статической ссылкой на Индикатор. Анкет Это обеспечивает инкапсулированное и, следовательно, многократно используемое решение для задач изменения ориентации. Это решение хорошо послужило мне для различных асинкнозных задач, включая загрузки в Интернете, общение с Услуги, и сканирование файловой системы. Решение было хорошо протестировано на нескольких версиях Android и моделях телефона. Полную демонстрацию можно найти здесь с особым интересом к Downloadfile.java

Я представляю следующее в качестве примера концепции

public class SimpleAsync extends AsyncTask<String, Integer, String> {
    private static ProgressDialog mProgressDialog = null;
    private final Context mContext;

    public SimpleAsync(Context context) {
        mContext = context;
        if ( mProgressDialog != null ) {
            onPreExecute();
        }
    }

    @Override
    protected void onPreExecute() {
        mProgressDialog = new ProgressDialog( mContext );
        mProgressDialog.show();
    }

    @Override
    protected void onPostExecute(String result) {
        if ( mProgressDialog != null ) {
            mProgressDialog.dismiss();
            mProgressDialog = null;
        }
    }

    @Override
    protected void onProgressUpdate(Integer... progress) {
        mProgressDialog.setProgress( progress[0] );
    }

    @Override
    protected String doInBackground(String... sUrl) {
        // Do some work here
        publishProgress(1);
        return null;
    }

    public void dismiss() {
        if ( mProgressDialog != null ) {
            mProgressDialog.dismiss();
        }
    }
}

Использование в активности Android просто

public class MainActivity extends Activity {
    DemoServiceClient mClient = null;
    DownloadFile mDownloadFile = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate( savedInstanceState );
        setContentView( R.layout.main );
        mDownloadFile = new DownloadFile( this );

        Button downloadButton = (Button) findViewById( R.id.download_file_button );
        downloadButton.setOnClickListener( new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mDownloadFile.execute( "http://www.textfiles.com/food/bakebred.txt");
            }
        });
    }

    @Override
    public void onPause() {
        super.onPause();
        mDownloadFile.dismiss();
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top