Come gestire la modifica dell'orientamento dello schermo quando la finestra di dialogo di avanzamento e il thread in background sono attivi?

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

Domanda

Il mio programma esegue alcune attività di rete in un thread in background.Prima di iniziare, viene visualizzata una finestra di dialogo di avanzamento.La finestra di dialogo viene chiusa sul gestore.Funziona tutto bene, tranne quando l'orientamento dello schermo cambia mentre la finestra di dialogo è attiva (e il thread in background è in corso).A questo punto l'app si arresta in modo anomalo, si blocca o entra in una fase strana in cui l'app non funziona affatto finché tutti i thread non vengono terminati.

Come posso gestire con garbo la modifica dell'orientamento dello schermo?

Il codice di esempio seguente corrisponde all'incirca a ciò che fa il mio programma reale:

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();
        }
    };
}

Pila:

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)

Ho provato a chiudere la finestra di dialogo di avanzamento in onSaveInstanceState, ma ciò impedisce solo un arresto anomalo immediato.Il thread in background è ancora in corso e l'interfaccia utente è in uno stato parzialmente disegnato.È necessario eliminare l'intera app prima che riprenda a funzionare.

È stato utile?

Soluzione

Quando si passa orientamenti, Android creerà una nuova vista. Probabilmente stai ricevendo crash perché il thread in background sta cercando di modificare lo stato su quello vecchio. (Può anche essere avendo difficoltà perché il thread in background non è sul thread UI)

Io suggerirei di fare quel mHandler volatile e aggiornarlo quando cambia l'orientamento.

Altri suggerimenti

Modificare: Gli ingegneri di Google sconsigliano questo approccio, come descritto da Dianne Hackborn (a.k.a. hackbod) in questo Articolo StackOverflow.Guardare questo post del blog per maggiori informazioni.


Devi aggiungere questo alla dichiarazione di attività nel manifest:

android:configChanges="orientation|screenSize"

quindi sembra

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

Il fatto è che il sistema distrugge l'attività quando si verifica un cambiamento nella configurazione.Vedere Modifiche alla configurazione.

Quindi inserirlo nel file di configurazione evita che il sistema distrugga la tua attività.Invece invoca il onConfigurationChanged(Configuration) metodo.

mi si avvicinò con una soluzione solida per questi problemi che si conforma con la 'Android Way' di cose. Ho tutta la mia lunga durata operazioni utilizzando il modello IntentService.

Cioè, le mie attività intenti trasmissione, l'IntentService fa il lavoro, salva i dati nel DB e poi le trasmissioni appiccicose intenti. La parte adesiva è importante, in modo tale che anche se l'attività è stata sospesa durante durante il tempo dopo che l'utente ha avviato il lavoro e manca il tempo reale in onda dal IntentService possiamo ancora rispondere e raccogliere i dati dalla chiamata Activity. ProgressDialogs possono lavorare con questo schema abbastanza bene con onSaveInstanceState().

In sostanza, è necessario salvare una bandiera che si dispone di una finestra di avanzamento in esecuzione nell'istanza fascio salvato. Non Salva la finestra di oggetto progresso perché questo perderà l'intera attività. Per avere un persistente ad finestra di avanzamento, ho conservarlo come riferimento debole nell'oggetto applicazione. Sul cambiamento di orientamento o qualsiasi altra cosa che fa sì che l'Attività di mettere in pausa (telefonata, utente preme casa, ecc) e poi riprendere, mi respingere la vecchia finestra di dialogo e ricreare una nuova finestra di dialogo in attività appena creata.

Per finestre di dialogo di progresso indefinito questo è facile. Per il progresso stile bar, bisogna mettere l'ultima relazione sull'andamento conosciuto nel bundle e tutte le informazioni che si sta utilizzando a livello locale nell'attività di tenere traccia dei progressi. Sul ripristino del progresso, che verrà utilizzato queste informazioni per ri-generare la barra di avanzamento nello stesso stato come prima e quindi aggiornare in base allo stato attuale delle cose.

Quindi, per riassumere, mettendo compiti a lungo in esecuzione in un IntentService accoppiato con uso giudizioso di onSaveInstanceState() consente di tenere efficacemente traccia delle finestre di dialogo e ripristinare poi attraverso gli eventi del ciclo di vita di attività. bit rilevanti di codice attività sono al di sotto. Avrete anche bisogno di logica nel vostro BroadcastReceiver per gestire intenti Sticky in modo appropriato, ma che è oltre la portata di questo.

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));
    }
}

Ho incontrato lo stesso problema. La mia attività ha bisogno di analizzare alcuni dati da un URL ed è lento. Così ho creato un filo di farlo, quindi mostrare una finestra di avanzamento. Ho lasciato il filo inviare un messaggio al thread dell'interfaccia utente tramite Handler quando è finito. In Handler.handleMessage, ottengo l'oggetto di dati (pronto ora) dal filo e popolarlo di interfaccia utente. Quindi è molto simile al vostro esempio.

Dopo un sacco di tentativi ed errori sembra che ho trovato una soluzione. Almeno ora posso ruotare lo schermo in qualsiasi momento, prima o dopo il filo è fatto. In tutti i test, la finestra di dialogo è ben chiuso ed tutti i comportamenti sono come previsto.

Quello che ho fatto è illustrato di seguito. L'obiettivo è di riempire il mio modello di dati (mDataObject) e poi popolarla di interfaccia utente. Dovrebbe consentire la rotazione dello schermo in qualsiasi momento senza sorpresa.

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.
    }
}

Questo è quello che funziona per me. Non so se questo è il metodo "corretto", come progettato da Android - essi sostengono questo "distruggere / ricreare l'attività durante la rotazione dello schermo", in realtà rende le cose più facili, quindi credo che non dovrebbe essere troppo difficile

Fatemi sapere se si vede un problema nel mio codice. Come detto sopra io non so davvero se non v'è alcun effetto collaterale.

Il problema originale percepito è che il codice non sarebbe sopravvissuta un cambiamento dell'orientamento dello schermo. A quanto pare questo è stato "risolto" avendo il programma di gestire il cambiamento orientamento dello schermo stesso, invece di lasciare che il framework di interfaccia utente farlo (via chiamando OnDestroy)).

vorrei far presente che, se il problema di fondo è che il programma non sopravviverà OnDestroy (), allora la soluzione accettata è solo una soluzione che lascia il programma con gravi altri problemi e vulnerabilità. Si ricorda che il quadro Android afferma esplicitamente che la vostra attività è a rischio di essere distrutto quasi in qualsiasi momento a causa di circostanze al di fuori del vostro controllo. Pertanto, l'attività deve essere in grado di sopravvivere OnDestroy () e successiva onCreate () per qualsiasi motivo, non è solo un cambiamento dell'orientamento dello schermo.

Se avete intenzione di accettare l'orientamento dello schermo movimentazione cambia te stesso per risolvere il problema del PO, è necessario verificare che le altre cause di OnDestroy () non comportano lo stesso errore. Siete in grado di fare questo? In caso contrario, mi chiedo se la risposta "accettato" è davvero un ottimo uno.

La mia soluzione era quella di estendere la classe ProgressDialog per ottenere la mia MyProgressDialog.
Ho ridefinito show() e dismiss() metodi per bloccare l'orientamento prima di mostrare il Dialog e sbloccarlo indietro quando Dialog è respinto. Così, quando il Dialog viene mostrato e l'orientamento delle modifiche del dispositivo, l'orientamento dello schermo rimane fino dismiss() viene chiamato, quindi schermo orientamento cambia secondo sensore valori / dispositivo-orientamento.

Qui è il mio codice:

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);
}

}

Ho affrontato questo stesso problema, e mi si avvicinò con una soluzione che non ha invole utilizzando il ProgressDialog e ottengo risultati più rapidi.

Quello che ho fatto è stato creare un layout che ha una ProgressBar in esso.

<?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>

Poi nel metodo onCreate effettuare le seguenti

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

Poi fare il compito a lungo in un filo, e quando questo è finito avere un set Runnable alla vista del contenuto al layout vero che si desidera utilizzare per questa attività.

Ad esempio:

mHandler.post(new Runnable(){

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

Questo è quello che ho fatto, e ho scoperto che corre più veloce che mostra il ProgressDialog ed è meno invasivo e ha un aspetto migliore a mio parere.

Tuttavia, se hai intenzione di utilizzare il ProgressDialog, allora questa risposta non è per voi.

ho scoperto una soluzione a questo che non ho ancora visto altrove. È possibile utilizzare un oggetto applicazione personalizzata che sa se si dispone di attività in background in corso, invece di cercare di fare questo nell'attività che viene distrutto e ricreato sul cambiamento di orientamento. Ho bloggato su questo in qui .

I intenzione di contribuire il mio approccio alla gestione di questo problema di rotazione. Questo potrebbe non essere relativo al PO come lui non sta usando AsyncTask, ma forse altri lo troveranno utile. E 'piuttosto semplice ma sembra di fare il lavoro per me:

Ho un attività di accesso con un nidificato classe AsyncTask chiamato BackgroundLoginTask.

Nel mio BackgroundLoginTask non faccio niente di straordinario se non per aggiungere un controllo nullo sulla chiamando ProgressDialog di respingere:

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

Questo è quello di gestire il caso in cui l'attività in background termina mentre la Activity non è visibile e, di conseguenza, la finestra di avanzamento è già stato respinto con il metodo onPause().

In seguito, nella mia classe genitore Activity, creo maniglie statiche globali per la mia classe e la mia AsyncTask ProgressDialog (il AsyncTask, essendo nidificati, possono accedere a queste variabili):

private static BackgroundLoginTask backgroundLoginTask;
private static ProgressDialog pleaseWaitDialog;

Questo serve a due scopi: in primo luogo, esso permette al mio Activity per accedere sempre all'oggetto AsyncTask anche da una nuova, attività post-ruotato. In secondo luogo, permette al mio BackgroundLoginTask per accedere e respingere il ProgressDialog anche dopo una rotazione.

Poi, aggiungo che questo onPause(), causando la finestra di avanzamento a scomparire quando il nostro Activity lascia il primo piano (evitando quella brutta "forza vicino" Crash):

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

Infine, Ho il seguente nel mio metodo onResume():

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

In questo modo il Dialog per riapparire dopo il Activity viene ricreata.

Ecco l'intera classe:

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();
            } 
        }
        }

    }
}

Sono in alcun modo uno sviluppatore Android stagionato, quindi sentitevi liberi di commentare.

Spostare il compito lungo a una classe separata. Implementarlo come un modello soggetto-osservatore. Ogni volta che l'attività viene creata registro e durante la chiusura annullare la registrazione con la classe compito. classe Task può utilizzare AsyncTask.

Il trucco è mostrare / chiudere la finestra entro AsyncTask durante OnPreExecute / OnPostExecute come al solito, se nel caso di orientamento cambiamento creare / visualizzare una nuova istanza della finestra nell'attività e passare il suo riferimento al compito.

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;
        }       
    }
}

L'ho fatto in questo modo:

    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 {

                }
            }
        }

    }

Si può anche cercare di farmi sapere che funziona per voi o no

Se si crea uno sfondo Service che fa tutto il lavoro pesante (richieste TCP / risposta, unmarshalling), il View e Activity possono essere distrutti e ricreato senza finestra perdite o perdita di dati. In questo modo il comportamento di Android, che è quello di distruggere un attività su ogni modifica della configurazione consigliata (ad es. per ogni variazione dell'orientamento).

E 'un po' più complesso, ma è il modo migliore per invocare richiesta del server, i dati pre / post-trattamento, ecc.

Si può anche utilizzare il Service fare la fila ogni richiesta a un server, in modo che rende facile ed efficiente per gestire queste cose.

La guida dev ha un pieno su Services .

Ho un'implementazione che consente l'attività per essere distrutto da un cambiamento dell'orientamento dello schermo, ma distrugge ancora la finestra di dialogo nell'attività ricreato con successo. Io uso ...NonConfigurationInstance per fissare il compito sfondo all'attività ricreato. gestisce il framework Android normale ricreare il dialogo stesso, nulla è cambiato lì.

I sottoclasse AsyncTask aggiunta di un campo per il 'possesso' di attività, e un metodo per aggiornare questo proprietario.

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);
  }

  ...
}

Nella mia classe di attività ho aggiunto un backgroundTask campo riferendosi al BackgroundTask 'di proprieta', e io aggiornare questo campo utilizzando onRetainNonConfigurationInstance e 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;
  }
  ...
}

Suggerimenti per ulteriori miglioramenti:

  • Cancella il riferimento backgroundTask in attività dopo il completamento dell'attività di rilasciare qualsiasi memoria o altre risorse ad esso associati.
  • Cancella il riferimento ownerActivity nel BackgroundTask prima che l'attività è distrutta nel caso non verrà ricreato immediatamente.
  • Crea un'interfaccia BackgroundTask e / o raccolta per consentire diversi tipi di attività da eseguire dalla stessa attività possessore.

Se si mantiene due layout, tutto thread UI dovrebbe essere terminato.

Se si utilizza AsynTask, allora si può facilmente chiamare .cancel() metodo all'interno metodo onDestroy() dell'attività corrente.

@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();
}

Per AsyncTask, saperne di più sezione "Annullamento di un compito" alla qui .

Aggiornamento: condizione Aggiunto per controllare lo stato, in quanto può essere annullato solo se è in stato di esecuzione. Si noti inoltre che l'AsyncTask può essere eseguito una sola volta.

Ho cercato di implementare jfelectron 'soluzione s, perché è un ' soluzione solida a questi problemi che si conforma con la 'Android Way' di cose ', ma ci sono voluti un po 'di tempo per cercare e mettere insieme tutti gli elementi citati. Finito con questo un po 'diverso, e credo più elegante, soluzione postato qui nella sua interezza.

Utilizza un IntentService sparato da un'attività per eseguire l'operazione in esecuzione a lungo su un thread separato. I fuochi di servizio posteriore appiccicoso intenti Broadcast per l'attività che la finestra di aggiornamento. L'attività utilizza showDialog (), onCreateDialog () e onPrepareDialog () per eliminare la necessità di disporre di dati persistenti passati nell'oggetto applicazione o il pacchetto savedInstanceState. Questo dovrebbe funzionare, non importa quanto l'applicazione viene interrotta.

Classe di attività:

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 Classe:

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);
    }
}}

voci di file manifesto:

prima sezione dell'applicazione:

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

all'interno della sezione applicazione

service android:name=".MyService"

Questa è la mia Soluzione proposta:

  • Spostare l'AsyncTask o discussione ad un frammento conservato, come spiegato qui . Credo che sia una buona pratica per spostare tutte le chiamate di rete a frammenti. Se si sta già utilizzando frammenti, uno di loro potrebbe essere fatto responsabile per le chiamate. In caso contrario, è possibile creare un frammento solo per fare la richiesta, come l'articolo collegato propone.
  • Il frammento userà un'interfaccia listener per segnalare il completamento compito / fallimento. Non devi preoccuparti per le modifiche di orientamento là. Il frammento avrà sempre il link corretto per la finestra di attività e sui progressi in corso può essere tranquillamente ripreso.
  • Fai la tua dialogo un membro della vostra classe progresso. In realtà si dovrebbe farlo per tutte le finestre di dialogo. Nel metodo onPause li si dovrebbe respingere, altrimenti si perderà una finestra sulla modifica della configurazione. Lo stato di occupato deve essere tenuto dal frammento. Quando il frammento è collegato all'attività, è possibile far apparire la finestra di dialogo di nuovo progresso, se la chiamata è ancora in esecuzione. Procedimento void showProgressDialog() può aggiungere l'interfaccia ascoltatore frammento-attività per questo scopo.

Ho affrontato la stessa situazione. Quello che ho fatto è stato ottenere una sola istanza per la finestra di dialogo di progresso l'intera applicazione.

In primo luogo, ho creato una classe DialogSingleton per ottenere una sola istanza (pattern Singleton)

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();
        }
    }
}

Come faccio vedere in questa classe, ho la finestra di dialogo come attributo di progresso. Ogni volta che ho bisogno di mostrare una finestra di avanzamento, ottengo l'istanza unica e creare un nuovo ProgressDialog.

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

Quando mi sono fatto con il compito di fondo, che io chiamo di nuovo l'istanza unica e respingere il suo dialogo.

DialogSingleton.GetInstance().DialogDismiss(this);

risparmio lo stato dell'attività di fondo nelle mie preferenze condivise. Quando ho ruotare lo schermo, mi chiedo se ho un'attività in esecuzione per questa attività: (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).

Quando inizio l'esecuzione di un compito di fondo:

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

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

Quando finisco di esecuzione di un compito di fondo:

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

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

Spero che aiuta.

Questa è una domanda molto vecchio che è venuto sulla barra laterale per qualche motivo.

Se l'attività di sfondo deve solo sopravvivere mentre l'attività è in primo piano, la soluzione "nuovo" è per ospitare il filo di fondo (o, preferibilmente, AsyncTask) in un mantenuto frammento , come descritto in questo guida per gli sviluppatori e numerose domande e risposte

Un frammento conservato sopravvive se l'attività è distrutto per una modifica della configurazione, ma non quando l'attività viene distrutto in background o pila avanti. Pertanto, il compito di fondo dovrebbe comunque essere interrotta se isChangingConfigurations() è falso in onPause().

Sono un più fresco in Android e ho provato questo e ha funzionato.

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);
        }
}

Ho provato di tutto. giorni trascorsi a sperimentare. Non volevo per bloccare l'attività di ruotare. Il mio scenario è stato:

  1. Una finestra di avanzamento che mostra informazioni dinamiche per l'utente. Es .: "Connessione al server ...", "Scaricare i dati ...", ecc.
  2. Un filo facendo la roba pesante e l'aggiornamento del dialogo
  3. Aggiornare l'interfaccia utente con i risultati alla fine.

Il problema era, quando si ruota lo schermo, ogni soluzione sul libro non è riuscita. Anche con la classe AsyncTask, che è il modo corretto di Android di affrontare queste situazioni. Quando si ruota lo schermo, il contesto attuale che il filo di partenza sta lavorando con, se n'è andato, e che scombina con la finestra di dialogo che sta mostrando. Il problema era sempre il dialogo, non importa quante trucchi ho aggiunto al codice (passando nuovi contesti di thread in esecuzione, mantenendo gli stati filo attraverso rotazioni, ecc ...). La complessità codice alla fine era sempre enorme e c'era sempre qualcosa che poteva andare storto.

L'unica soluzione che ha funzionato per me era l'Attività / trucco di dialogo. È semplice e geniale ed è tutto di rotazione prova:

  1. Invece di creare un dialogo e chiedere di mostrarlo, creare un'attività che è stato impostato nel manifesto con Android: theme = "@ Android: Stile / Theme.Dialog". Così, appena si presenta come una finestra di dialogo.

  2. Sostituire showDialog (DIALOG_ID) con startSubActivity (yourActivityDialog, yourcode);

  3. Usa onActivityResult nella chiamata di attività per ottenere i risultati dal thread in esecuzione (anche gli errori) e aggiornare l'interfaccia utente.

  4. sulla 'ActivityDialog', fili di uso o AsyncTask di eseguire le attività lunghe e onRetainNonConfigurationInstance per salvare lo stato "dialogo" durante la rotazione dello schermo.

Questo è veloce e funziona bene. Io uso ancora le finestre di dialogo per altri compiti e l'AsyncTask per qualcosa che non richiede un dialogo costante sullo schermo. Ma con questo scenario, vado sempre per l'attività / modello di dialogo.

E, non ho provato, ma è anche possibile bloccare quell'attività / Dialog rotazione, quando il thread è in funzione, accelerando le cose, pur consentendo la chiamata attività per ruotare.

In questi giorni c'è un modo molto più distinta per gestire questi tipi di problemi. L'approccio tipico è:

1. Assicurarsi che i dati siano correttamente separata dall'interfaccia utente:

Tutto ciò che è un processo in background dovrebbe essere in un Fragment conservato (impostare questo con Fragment.setRetainInstance(). Questo diventa il vostro 'archiviazione dei dati persistenti' in cui i dati nulla based che gradireste conservato viene mantenuto. Dopo l'evento di modifica dell'orientamento, questo Fragment sarà essere ancora accessibile nel suo stato originale attraverso una chiamata FragmentManager.findFragmentByTag() (quando lo si crea si dovrebbe dare un tag non è un ID in quanto non è collegata a un View).

Vedere la Handling guida sviluppato Modifiche runtime per informazioni su questa operazione in modo corretto e perché è l'opzione migliore.

2. Assicurarsi che si sta interfacciando corretto e sicuro tra lo sfondo processs e l'interfaccia utente:

È necessario inversa il tuo processo di collegamento. Al momento il vostro processo in background si attacca a un View - invece la tua View deve essere a sua volta connesso al processo di sfondo. Ha più senso giusto? L'azione del View dipende dal processo in background, considerando che il processo di fondo non dipende dai mezzi View.This cambiando il collegamento a un'interfaccia Listener standard. Di 'la tua processo (qualsiasi classe che è - se si tratta di AsyncTask, Runnable o qualsiasi altra cosa). Definisce un OnProcessFinishedListener, quando il processo è fatto dovrebbe chiamare che ascoltatore se esiste

risposta è una bella descrizione sintetica del come fare listener personalizzati.

3. Collega il tuo interfaccia utente nel processo di dati ogni volta che si crea l'interfaccia utente (compresi i cambiamenti di orientamento):

Ora si deve preoccupare di interfacciare il compito sfondo con qualunque sia la vostra struttura View corrente è. Se si sta gestendo le modifiche di orientamento correttamente (non il configChanges incidere persone raccomandano sempre), allora il vostro Dialog verrà ricreato dal sistema. Questo è importante, significa che sul cambiamento di orientamento, i metodi del ciclo di vita di tutta la vostra Dialog vengono richiamati. Quindi, in uno di questi metodi (onCreateDialog di solito è un buon posto), si potrebbe fare una chiamata come il seguente:

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

Vedere la Frammento del ciclo di vita per decidere dove impostare l'ascoltatore migliore si inserisce nella vostra implementazione individuale.

Si tratta di un approccio generale a fornire una soluzione robusta e completa al problema generica chiesto a questa domanda. C'è probabilmente un paio di pezzi minori mancanti in questa risposta a seconda dello scenario individuale, ma questo è generalmente l'approccio più corretto per gestire correttamente gli eventi di modifica orientamento.

ho trovato e la soluzione più facile da gestire thread quando il cambiamento di orientamento. Si può solo tenere un riferimento statico per la vostra attività / frammento e verificare se nulla prima di agire sull'interfaccia utente. Io suggerisco di usare un tentativo di cattura troppo:

 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;
    }


}

Se stai lottando con il rilevamento eventi di modifica l'orientamento di una finestra di dialogo INDIPENDENTE DI RIFERIMENTO ATTIVITA , questo metodo funziona bene eccitante. Io uso questo perché ho il mio classe finestra di dialogo che possono essere visualizzati in molteplici attività diverse in modo da non so sempre quale l'attività è in fase mostrato in. Con questo metodo non è necessario cambiare l'AndroidManifest, preoccuparsi di riferimenti di attività, e non hai bisogno di una finestra personalizzata (come ho). Si ha bisogno, tuttavia, una visione di contenuti personalizzati in modo da poter rilevare l'orientamento cambia usando quel particolare punto di vista. Ecco il mio esempio:

Configurazione

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

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

        //DO SOMETHING HERE!! :D
    }
}

Esecuzione 1 - Dialog

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

Attuazione 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();

Attuazione 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();

Questa è la mia soluzione quando ho dovuto affrontare è: ProgressDialog non è un bambino Fragment, quindi il mio personalizzato di classe "ProgressDialogFragment" può estendere DialogFragment invece al fine di mantenere la finestra di dialogo mostrata per le modifiche di configurazione.

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();
        }
    }
}
  

La sfida era quella di mantenere il titolo di dialogo e messaggio mentre schermo   rotazione mentre resettati alla stringa vuota predefinito, anche se la finestra ancora visualizzato

Ci sono 2 approcci per risolvere questo:

Primo approccio: Fare l'attività che utilizza la finestra di dialogo per mantenere lo stato durante il cambiamento di configurazione nel file manifesto:

android:configChanges="orientation|screenSize|keyboardHidden"

Questo approccio non è preferito da parte di Google.

Secondo approccio: sul metodo di onCreate() la propria attività, è necessario mantenere il vostro DialogFragment ricostruendo nuovamente il ProgressDialogFragment con il titolo e messaggio come segue se la savedInstanceState non è nullo:

@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");
      }
  }
}

Sembra troppo 'veloce e sporco' per essere vero quindi per favore notare i difetti, ma quello che ho trovato era funzionato ...

All'interno del metodo OnPostExecute della mia AsyncTask, ho semplicemente avvolto il' .dismiss' per la finestra di avanzamento in un blocco try / catch (con una cattura vuoto) e poi semplicemente ignorato l'eccezione che è stata sollevata. Sembra sbagliato da fare, ma sembra non ci sono effetti negativi (almeno per quello che sto facendo in seguito che è quello di avviare un'altra attività passando il risultato della mia interrogazione corsa lunga come un extra)

La soluzione più semplice e più flessibile è quello di utilizzare un AsyncTask con un riferimento statico per ProgressBar . Questo fornisce una soluzione incapsulato e quindi riutilizzabile per problemi di cambiamento di orientamento. Questa soluzione mi ha servito bene per variare compiti asincroni tra cui download da Internet, comunicare con i Servizi e scansioni filesystem. La soluzione è stata ben testato su più versioni di Android e modelli di telefono. Una demo completo è disponibile rel="nofollow"> con interesse specifico in DownloadFile.java

presento la seguente come esempio concetto

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();
        }
    }
}

Utilizzo in un Android di attività è semplice

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();
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top