Como lidar com a mudança de orientação da tela quando a caixa de diálogo de progresso e o thread de segundo plano estão ativos?

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

Pergunta

Meu programa realiza alguma atividade de rede em um thread em segundo plano.Antes de começar, aparece uma caixa de diálogo de progresso.A caixa de diálogo é descartada no manipulador.Tudo isso funciona bem, exceto quando a orientação da tela muda enquanto a caixa de diálogo está ativa (e o thread de segundo plano está em andamento).Nesse ponto, o aplicativo trava, trava ou entra em um estágio estranho em que o aplicativo não funciona até que todos os threads sejam eliminados.

Como posso lidar com a mudança de orientação da tela normalmente?

O código de exemplo abaixo corresponde aproximadamente ao que meu programa real faz:

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

Pilha:

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)

Tentei descartar a caixa de diálogo de progresso em onSaveInstanceState, mas isso apenas evita uma falha imediata.O thread em segundo plano ainda está em andamento e a IU está parcialmente desenhada.Precisa encerrar todo o aplicativo antes que ele comece a funcionar novamente.

Foi útil?

Solução

Quando você troca de orientações, o Android cria uma nova visualização. Você provavelmente está recebendo falhas porque seu tópico de plano de fundo está tentando alterar o estado no antigo. (Também pode estar tendo problemas porque seu tópico de fundo não está no tópico da interface do usuário)

Eu sugiro fazer com que o Mhandler volátil e atualize quando a orientação mudar.

Outras dicas

Editar: Os engenheiros do Google não recomendam essa abordagem, conforme descrito por Dianne Hackborn (também conhecido como hackbod) nisso Postagem de Stackoverflow. Verificação de saída esta postagem do blog Para maiores informações.


Você deve adicionar isso à declaração de atividade no manifesto:

android:configChanges="orientation|screenSize"

Então parece

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

O assunto é que o sistema destrói a atividade quando ocorre uma alteração na configuração. Ver ConfigurationChanges.

Portanto, colocar isso no arquivo de configuração evita o sistema para destruir sua atividade. Em vez disso, invoca o onConfigurationChanged(Configuration) método.

Eu criei uma solução sólida para esses problemas que estão em conformidade com o 'Android Way' das coisas. Eu tenho todas as minhas operações de longa duração usando o padrão IntentService.

Isto é, minhas atividades transmitem intenções, o intentsservice faz o trabalho, salva os dados no banco de dados e depois transmite pegajoso intenções. A parte pegajosa é importante, de modo que, mesmo que a atividade tenha sido interrompida durante o tempo depois que o usuário iniciou o trabalho e ergue a transmissão em tempo real do IntentService, ainda podemos responder e adquirir os dados da atividade de chamada. ProgressDialogs pode trabalhar com esse padrão muito bem com onSaveInstanceState().

Basicamente, você precisa salvar um sinalizador que você tem uma caixa de diálogo de progresso em execução no pacote de instância salvo. Não faça Salve o objeto de diálogo de progresso porque isso vazará toda a atividade. Para ter um identificador persistente na caixa de diálogo Progresso, eu o guardo como uma referência fraca no objeto de aplicação. Na mudança de orientação ou em qualquer outra coisa que faça com que a atividade pause (telefonema, usuário atinge a casa etc.) e depois retomo, descarto a caixa de diálogo antiga e recrio uma nova caixa de diálogo na atividade recém -criada.

Para diálogos de progresso indefinido, isso é fácil. Para o estilo da barra de progresso, você deve colocar o último progresso conhecido no pacote e as informações que você está usando localmente na atividade para acompanhar o progresso. Ao restaurar o progresso, você usará essas informações para re-divulgar a barra de progresso no mesmo estado de antes e depois atualizará com base no estado atual das coisas.

Então, para resumir, colocando tarefas de longa duração em um serviço intentário juntamente com o uso criterioso de onSaveInstanceState() Permite acompanhar com eficiência os diálogos e restaurar os eventos do ciclo de vida da atividade. Os bits relevantes do código de atividade estão abaixo. Você também precisará de lógica no seu BroadCastreceiver para lidar com as intenções pegajosas adequadamente, mas isso está além do escopo disso.

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

Eu conheci o mesmo problema. Minha atividade precisa analisar alguns dados de um URL e é lento. Então, eu crio um tópico para fazer isso e mostro uma caixa de diálogo de progresso. Eu deixei o tópico postar uma mensagem de volta ao tópico da interface do usuário via Handler Quando terminar. Dentro Handler.handleMessage, Recebo o objeto de dados (pronto agora) do thread e o preencho na interface do usuário. Portanto, é muito semelhante ao seu exemplo.

Depois de muita tentativa e erro, parece que encontrei uma solução. Pelo menos agora eu posso girar a tela a qualquer momento, antes ou depois que o thread estiver pronto. Em todos os testes, a caixa de diálogo está devidamente fechada e todos os comportamentos são os esperados.

O que fiz foi mostrado abaixo. O objetivo é preencher meu modelo de dados (mDataObject) e depois preencha -o na interface do usuário. Deve permitir a rotação da tela a qualquer momento sem surpresa.

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

É isso que funciona para mim. Não sei se esse é o método "correto", como projetado pelo Android - eles afirmam que isso "destruir/recriar a atividade durante a rotação da tela" realmente facilita as coisas, então acho que não deve ser muito complicado.

Deixe -me saber se você vir um problema no meu código. Como dito acima, eu realmente não sei se há algum efeito colateral.

O problema percebido original era que o código não sobreviveria a uma mudança de orientação da tela. Aparentemente, isso foi "resolvido", fazendo com que o programa lide com a mudança de orientação da tela, em vez de deixar a estrutura da interface do usuário fazer isso (via Calling OnDestroy)).

Eu alegaria que, se o problema subjacente é que o programa não sobreviverá a OnDestroy (), a solução aceita é apenas uma solução alternativa que deixa o programa com outros problemas e vulnerabilidades sérias. Lembre -se de que a estrutura do Android afirma especificamente que sua atividade corre o risco de ser destruída quase a qualquer momento devido a circunstâncias fora do seu controle. Portanto, sua atividade deve ser capaz de sobreviver a OnDestroy () e subsequente onCreate () por qualquer motivo, não apenas uma mudança de orientação da tela.

Se você aceitar que a orientação da tela de manuseio mude para resolver o problema do OP, você precisa verificar se outras causas do OnDestroy () não resultam no mesmo erro. Você pode fazer isso? Caso contrário, eu questionaria se a resposta "aceita" é realmente muito boa.

Minha solução foi estender o ProgressDialog classe para conseguir o meu MyProgressDialog.
Eu redefini show() e dismiss() métodos para bloquear a orientação antes de mostrar o Dialog e desbloqueá -lo de volta quando Dialog é demitido. Então, quando o Dialog é mostrado e a orientação do dispositivo muda, a orientação da tela permanece até dismiss() é chamado, então a orientação da tela muda de acordo com os valores de sensores/orientação do dispositivo.

Aqui está o meu código:

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

}

Enfrentei o mesmo problema e encontrei uma solução que não invole usando o ProgressDialog e obtive resultados mais rápidos.

O que eu fiz foi criar um layout que tenha uma barra de progresso.

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

Então, no método onCreate, faça o seguinte

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

Em seguida, faça a longa tarefa em um tópico e, quando terminar, tenha uma exibição de conteúdo executável para o layout real que você deseja usar para esta atividade.

Por exemplo:

mHandler.post(new Runnable(){

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

Foi o que fiz, e descobri que ele corre mais rápido do que mostrar o ProgressDialog e é menos intrusivo e tem uma visão melhor na minha opinião.

No entanto, se você deseja usar o ProgressDialog, essa resposta não é para você.

Descobri uma solução para isso que ainda não vi em outro lugar. Você pode usar um objeto de aplicativo personalizado que saiba se você tem tarefas de segundo plano, em vez de tentar fazer isso na atividade que é destruída e recriada na mudança de orientação. Eu escrevi sobre isso em aqui.

Vou contribuir com minha abordagem para lidar com esse problema de rotação. Isso pode não ser relevante para o OP, pois ele não está usando AsyncTask, mas talvez outros achem útil. É bem simples, mas parece fazer o trabalho para mim:

Eu tenho uma atividade de login com um aninhado AsyncTask classe chamada BackgroundLoginTask.

No meu BackgroundLoginTask Eu não faço nada fora do comum, exceto para adicionar uma verificação nula ao ligar ProgressDialogé dispensado:

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

Isso é para lidar com o caso em que a tarefa de fundo termina enquanto o Activity não é visível e, portanto, a caixa de diálogo de progresso já foi demitida pelo onPause() método.

Em seguida, em meus pais Activity aula, eu crio alças estáticas globais para o meu AsyncTask classe e minha ProgressDialog (a AsyncTask, sendo aninhado, pode acessar essas variáveis):

private static BackgroundLoginTask backgroundLoginTask;
private static ProgressDialog pleaseWaitDialog;

Isso serve dois propósitos: primeiro, permite meu Activity para sempre acessar o AsyncTask Objeto mesmo de uma nova atividade pós-rotativa. Segundo, permite meu BackgroundLoginTask para acessar e descartar o ProgressDialog Mesmo depois de girar.

Em seguida, adiciono isso a onPause(), fazendo com que a caixa de diálogo de progresso desapareça quando nosso Activity está deixando o primeiro plano (impedindo aquele acidente feio de "força fechada"):

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

Finalmente, eu tenho o seguinte no meu onResume() método:

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

Isso permite o Dialog para reaparecer após o Activity é recriado.

Aqui está toda a 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();
            } 
        }
        }

    }
}

Não sou de forma alguma um desenvolvedor experiente do Android, então fique à vontade para comentar.

Mova a longa tarefa para uma classe separada. Implemente-o como um padrão de objeto-observador. Sempre que a atividade é criada registro e, ao mesmo tempo, fecha o registro da classe de tarefas. A classe de tarefas pode usar asyncTask.

O truque é mostrar/descartar a caixa de diálogo dentro do AsyncTask durante o OnPreexecute/OnPostexecute, como de costume, embora em caso de mudança de orientação crie/mostre uma nova instância da caixa de diálogo na atividade e passe sua referência à tarefa.

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

Eu fiz assim:

    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 {

                }
            }
        }

    }

Você também pode tentar me informar que funciona para você ou não

Se você criar um fundo Service Isso faz todo o levantamento pesado (solicitações/respostas TCP, UnarShalling), o View e Activity pode ser destruído e recriado sem vazamento de janelas ou perder dados. Isso permite o comportamento recomendado do Android, que é para Destrua uma atividade em cada mudança de configuração (por exemplo, para cada mudança de orientação).

É um pouco mais complexo, mas é a melhor maneira de chamar a solicitação do servidor, dados pré/pós-processamento, etc.

Você pode até usar o seu Service Para fazer fila, cada solicitação para um servidor, para facilitar e eficiente lidar com essas coisas.

O guia de desenvolvimento tem um completo capítulo em Services.

Tenho uma implementação que permite que a atividade seja destruída em uma mudança de orientação da tela, mas ainda destrói o diálogo na atividade recriada com sucesso. eu uso ...NonConfigurationInstance Para anexar a tarefa de segundo plano à atividade recriada. A estrutura normal do Android manipula recriando a própria caixa de diálogo, nada é alterado lá.

Subclassei o AsyncTask adicionando um campo para a atividade 'possuindo' e um método para atualizar esse proprietário.

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

  ...
}

Na minha aula de atividades, adicionei um campo backgroundTask referindo -se ao toque de fundo 'de propriedade', e eu atualizo este campo usando 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;
  }
  ...
}

Sugestões para melhorias adicionais:

  • Limpar o backgroundTask Referência na atividade após a conclusão da tarefa para liberar qualquer memória ou outros recursos associados a ela.
  • Limpar o ownerActivity Referência no cenário de fundo antes que a atividade seja destruída, caso não seja recriada imediatamente.
  • Crie um BackgroundTask Interface e/ou coleção para permitir que diferentes tipos de tarefas sejam executados da mesma atividade própria.

Se você mantiver dois layouts, todo o encadeamento da interface do usuário deve ser encerrado.

Se você usa asyntask, poderá ligar facilmente .cancel() método dentro onDestroy() Método de atividade atual.

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

Para assínctask, leia mais em "Cancelar uma tarefa" na seção em aqui.

Atualizar:Condição adicionada para verificar o status, pois só pode ser cancelado se estiver no estado de corrida. Observe também que o AsyncTask só pode ser executado uma vez.

Tentou implementar JFELECRONCETRONSolução porque é um "Solução sólida para esses problemas que estão em conformidade com o 'Android Way' das coisas"Mas demorou algum tempo para olhar para cima e montar todos os elementos mencionados. Acabou com isso um pouco diferente, e acho que mais elegante, a solução postada aqui na íntegra.

Usa um serviço intentário disparado de uma atividade para executar a tarefa de longa execução em um encadeamento separado. O serviço dispara de volta intensificações de transmissão aderentes à atividade que atualiza a caixa de diálogo. A atividade usa o showDialog (), OnCreatedialog () e onPreparedialog () para eliminar a necessidade de ter dados persistentes no objeto de aplicação ou no pacote SAVEDINSTACESTATE. Isso deve funcionar, não importa como seu aplicativo seja interrompido.

Classe de atividade:

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

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

Entradas de arquivo de manifesto:

Antes da seção de aplicação:

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

Seção de aplicação interna

service android:name=".MyService"

Esta é a minha solução proposta:

  • Mova a assínceta ou fio para um fragmento retido, conforme explicado aqui. Eu acredito que é uma boa prática mover todas as chamadas de rede para fragmentos. Se você já está usando fragmentos, um deles pode ser responsabilizado pelas chamadas. Caso contrário, você pode criar um fragmento apenas para fazer a solicitação, como propõe o artigo vinculado.
  • O fragmento usará uma interface do ouvinte para sinalizar a conclusão/falha da tarefa. Você não precisa se preocupar com as mudanças de orientação lá. O fragmento sempre terá o link correto para a atividade atual e a caixa de diálogo de progresso pode ser retomada com segurança.
  • Faça seu diálogo de progresso um membro da sua classe. Na verdade, você deve fazer isso para todos os diálogos. No método OnPause, você deve descartá -los, caso contrário, vazará uma janela para a alteração da configuração. O estado ocupado deve ser mantido pelo fragmento. Quando o fragmento é anexado à atividade, você poderá trazer à tona a caixa de diálogo Progresso, se a chamada ainda estiver em execução. UMA void showProgressDialog() O método pode ser adicionado à interface do ouvinte de atividades de fragmento para esse fim.

Eu enfrentei a mesma situação. O que fiz foi obter apenas uma instância para minha caixa de diálogo de progresso em todo o aplicativo.

Primeiro, criei uma classe de diálogo para obter apenas uma instância (padrão 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();
        }
    }
}

Como mostro nesta classe, tenho a caixa de diálogo Progresso como atributo. Toda vez que preciso mostrar uma caixa de diálogo de progresso, recebo a instância exclusiva e crio um novo ProgressDialog.

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

Quando terminar a tarefa de segundo plano, chamo novamente a instância exclusiva e descarto sua caixa de diálogo.

DialogSingleton.GetInstance().DialogDismiss(this);

Eu salvo o status da tarefa de fundo em minhas preferências compartilhadas. Quando eu giro a tela, pergunto se tenho uma tarefa em execução para esta atividade: (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 começo a executar uma tarefa em segundo plano:

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

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

Quando termino de executar uma tarefa em segundo plano:

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

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

Espero que ajude.

Esta é uma pergunta muito antiga que surgiu na barra lateral por algum motivo.

Se a tarefa de fundo só precisar sobreviver enquanto a atividade estiver em primeiro plano, a solução "nova" é hospedar o thread de fundo (ou, de preferência, AsyncTask) em um fragmento retido, como descrito neste Guia do desenvolvedor e numerosos perguntas e perguntas e respostas.

Um fragmento retido sobrevive se a atividade for destruída para uma mudança de configuração, mas não Quando a atividade é destruída em segundo plano ou pilha traseira. Portanto, a tarefa de segundo plano ainda deve ser interrompida se isChangingConfigurations() é falso em onPause().

Sou mais fresco no Android e tentei isso e funcionou.

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

Eu tentei de TUDO.Passei dias experimentando.Eu não queria impedir a rotação da atividade.Meu cenário foi:

  1. Uma caixa de diálogo de progresso mostrando informações dinâmicas ao usuário.Por exemplo.:"Conectando ao servidor...", "Baixando dados...", etc.
  2. Um tópico fazendo coisas pesadas e atualizando a caixa de diálogo
  3. Atualizando a UI com os resultados no final.

O problema era que, ao girar a tela, todas as soluções do livro falhavam.Mesmo com a classe AsyncTask, que é a maneira correta do Android de lidar com essas situações.Ao girar a tela, o Contexto atual com o qual o thread inicial está trabalhando desaparece e isso atrapalha a caixa de diálogo que está sendo exibida.O problema sempre foi o Dialog, não importa quantos truques eu adicionei ao código (passar novos contextos para threads em execução, reter estados de threads através de rotações, etc...).A complexidade do código no final sempre foi enorme e sempre havia algo que poderia dar errado.

A única solução que funcionou para mim foi o truque de Atividade/Diálogo.É simples e genial e é totalmente à prova de rotação:

  1. Em vez de criar um Dialog e pedir para mostrá-lo, crie uma Activity que foi definida no manifesto com android:theme="@android:style/Theme.Dialog".Então, parece apenas um diálogo.

  2. Substitua showDialog(DIALOG_ID) por startActivityForResult(yourActivityDialog, yourCode);

  3. Use onActivityResult na atividade de chamada para obter os resultados do thread em execução (até mesmo os erros) e atualizar a IU.

  4. Em seu 'ActivityDialog', use threads ou AsyncTask para executar tarefas longas e onRetainNonConfigurationInstance para salvar o estado de "caixa de diálogo" ao girar a tela.

Isso é rápido e funciona bem.Ainda uso diálogos para outras tarefas e AsyncTask para algo que não requer um diálogo constante na tela.Mas com esse cenário, eu sempre opto pelo padrão Atividade/Diálogo.

E eu não tentei, mas é até possível bloquear a rotação daquela Atividade/Diálogo, quando o thread está em execução, acelerando as coisas, ao mesmo tempo que permite a rotação da Atividade de chamada.

Hoje em dia, há uma maneira muito mais distinta de lidar com esses tipos de problemas. A abordagem típica é:

1. Verifique se seus dados estão devidamente separados da interface do usuário:

Qualquer coisa que seja um processo de fundo deve estar em um retido Fragment (Defina isso com Fragment.setRetainInstance(). Isso se torna o seu 'armazenamento persistente de dados', onde qualquer coisa baseada em dados que você gostaria que é mantida é mantida. Após o evento de mudança de orientação, este Fragment ainda estará acessível em seu estado original através de um FragmentManager.findFragmentByTag() Ligue (quando você o criar, você deve dar uma tag não é um id como não está anexado a um View).

Veja o Lidar com mudanças de tempo de execução Guia desenvolvido para obter informações sobre como fazer isso corretamente e por que é a melhor opção.

2. Certifique -se de que você está interagindo corretamente e com segurança entre os processos em segundo plano e sua interface do usuário:

Você deve marcha ré seu processo de vinculação. No momento, seu processo de fundo se liga a um View - em vez disso View deve estar se anexando ao processo de segundo plano. Faz mais sentido, certo? o ViewA ação de depende do processo de fundo, enquanto o processo de fundo não depende do View. Isso significa mudar o link para um padrão Listener interface. Diga seu processo (qualquer que seja a classe - seja um AsyncTask, Runnable ou o que quer que) defina um OnProcessFinishedListener, quando o processo é feito, deve chamar esse ouvinte se existir.

este responda é uma descrição concisa e agradável de como fazer ouvintes personalizados.

3. Ligue sua interface do usuário ao processo de dados sempre que a interface do usuário for criada (incluindo alterações de orientação):

Agora você deve se preocupar em interface a tarefa de fundo com qualquer que seja o seu atual View estrutura é. Se você está lidando com suas mudanças de orientação devidamente (não o configChanges hackear as pessoas sempre recomendam), então o seu Dialog será recriado pelo sistema. Isso é importante, significa que, na mudança de orientação, todo o seu DialogOs métodos do ciclo de vida são lembrados. Então, em qualquer um desses métodos (onCreateDialog geralmente é um bom lugar), você pode fazer uma chamada como a seguinte:

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

Veja o Ciclo de vida do fragmento Para decidir onde definir o ouvinte melhor se encaixa em sua implementação individual.

Esta é uma abordagem geral para fornecer uma solução robusta e completa para o problema genérico feito nesta pergunta. Provavelmente, existem algumas peças menores nessa resposta, dependendo do seu cenário individual, mas essa geralmente é a abordagem mais correta para lidar adequadamente com os eventos de mudança de orientação.

Encontrei e a solução mais fácil de manusear os threads quando a mudança de orientação. Você pode apenas manter uma referência estática à sua atividade/fragmento e verificar se é nulo antes de agir na interface do usuário. Eu sugiro usar uma pegada de tentativa também:

 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 você está lutando para detectar eventos de mudança de orientação de uma caixa de diálogo Independente de uma referência de atividade, esse método funciona muito bem. Eu uso isso porque tenho minha própria classe de diálogo que pode ser mostrada em várias atividades diferentes, então nem sempre sei em que atividade ela está sendo mostrada. Com esse método, você não precisa alterar o AndroidManifest, se preocupar com referências de atividade, E você não precisa de uma caixa de diálogo personalizada (como eu). Você precisa, no entanto, uma visualização de conteúdo personalizada para detectar as alterações de orientação usando essa visualização específica. Aqui está o meu exemplo:

Configurar

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

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

        //DO SOMETHING HERE!! :D
    }
}

Implementação 1 - diálogo

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

Implementação 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();

Implementação 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();

Esta é a minha solução quando eu a enfrentei:ProgressDialog não é um Fragment criança, então minha aula personalizada "ProgressDialogFragment"Pode se estender DialogFragment Em vez disso, para manter a caixa de diálogo mostrada para alterações de configuração.

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

O desafio era manter o título e a mensagem de diálogo enquanto a rotação da tela é redefinida para a sequência vazia padrão, embora a caixa de diálogo ainda seja mostrada

Existem 2 abordagens para resolver isso:

Primeira abordagem: Faça a atividade que utiliza a caixa de diálogo para reter o estado durante a mudança de configuração no arquivo de manifesto:

android:configChanges="orientation|screenSize|keyboardHidden"

Essa abordagem não é preferida pelo Google.

Segunda abordagem:na atividade onCreate() Método, você precisa manter seu DialogFragment reconstruindo o ProgressDialogFragment novamente com o título e mensagem da seguinte savedInstanceState não é nulo:

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

Parece muito 'rápido e sujo' para ser verdade, então, por favor, aponte as falhas, mas o que eu achei que funcionou foi ...

Dentro do método onPostExecute do meu assíncrono, eu simplesmente envolvi o '.dismiss' para a caixa de diálogo Progresso em um bloco de tentativa/captura (com uma captura vazia) e simplesmente ignorei a exceção que foi levantada. Parece errado, mas parece que não há efeitos negativos (pelo menos pelo que estou fazendo posteriormente, que é iniciar outra atividade que passa no resultado da minha consulta longa como um extra)

A solução mais simples e flexível é usar um ASYNCTASK com uma referência estática a Barra de progresso. Isso fornece uma solução encapsulada e, portanto, reutilizável para os problemas de mudança de orientação. Esta solução me serviu bem para tarefas assíncronas variadas, incluindo downloads da Internet, comunicando -se com Serviços, e varreduras do sistema de arquivos. A solução foi bem testada em várias versões Android e modelos de telefone. Uma demonstração completa pode ser encontrada aqui com interesse específico em Downloadfile.java

Apresento o seguinte como exemplo conceitual

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

O uso em uma atividade do Android é simples

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();
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top