webentwicklung-frage-antwort-db.com.de

Wie gehe ich mit einer Änderung der Bildschirmausrichtung um, wenn der Fortschrittsdialog und der Hintergrund-Thread aktiv sind?

Mein Programm führt einige Netzwerkaktivitäten in einem Hintergrundthread aus. Vor dem Start wird ein Fortschrittsdialog angezeigt. Der Dialog wird auf dem Handler geschlossen. Dies alles funktioniert einwandfrei, außer wenn sich die Bildschirmausrichtung ändert, während das Dialogfeld geöffnet ist (und der Hintergrund-Thread läuft). Zu diesem Zeitpunkt stürzt die App entweder ab oder blockiert oder gerät in eine seltsame Phase, in der die App überhaupt nicht funktioniert, bis alle Threads beendet wurden.

Wie kann ich die Änderung der Bildschirmausrichtung ordnungsgemäß handhaben?

Der folgende Beispielcode entspricht in etwa dem, was mein echtes Programm tut:

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

Stapel:

E/WindowManager(  244): Activity MyAct has leaked window [email protected] that was originally added here
E/WindowManager(  244): Android.view.WindowLeaked: Activity MyAct has leaked window [email protected] 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)

Ich habe versucht, den Fortschrittsdialog in onSaveInstanceState zu schließen, aber das verhindert nur einen sofortigen Absturz. Der Hintergrund-Thread läuft noch und die Benutzeroberfläche ist teilweise gezeichnet. Sie müssen die gesamte App beenden, bevor sie wieder funktioniert.

510
Heikki Toivonen

Wenn Sie die Ausrichtung ändern, wird Android eine neue Ansicht erstellen. Wahrscheinlich kommt es zu Abstürzen, weil Ihr Hintergrundthread versucht, den Status der alten zu ändern. (Möglicherweise treten auch Probleme auf, weil Ihr Hintergrund-Thread befindet sich nicht im UI-Thread.)

Ich würde vorschlagen, diesen mHandler flüchtig zu machen und zu aktualisieren, wenn sich die Ausrichtung ändert.

149
haseman

Bearbeiten: Google-Ingenieure empfehlen diesen Ansatz nicht, wie von Dianne Hackborn (aka hackbod ) in diesem StackOverflow post . Check out dieser Blog-Post für weitere Informationen.


Sie müssen dies der Aktivitätserklärung im Manifest hinzufügen:

Android:configChanges="orientation|screenSize"

so sieht es aus

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

Die Sache ist, dass das System die Aktivität zerstört, wenn eine Änderung in der Konfiguration auftritt. Siehe ConfigurationChanges .

Wenn Sie dies in die Konfigurationsdatei aufnehmen, wird vermieden, dass das System Ihre Aktivität zerstört. Stattdessen wird die Methode onConfigurationChanged(Configuration) aufgerufen.

258
sonxurxo

Ich habe eine grundsolide Lösung für diese Probleme gefunden, die dem „Android Way“ der Dinge entspricht. Ich habe alle meine langfristigen Vorgänge mit dem IntentService-Muster.

Das heißt, meine Aktivitäten senden Absichten, der IntentService erledigt die Arbeit, speichert die Daten in der Datenbank und sendet dann sticky Absichten. Der klebrige Teil ist wichtig, damit wir auch dann antworten und die Daten aus der aufrufenden Aktivität abrufen können, wenn die Aktivität während der Zeit, nachdem der Benutzer die Arbeit begonnen hat, angehalten wurde und die Echtzeitübertragung vom IntentService verpasst wurde. ProgressDialogs kann mit diesem Muster sehr gut mit onSaveInstanceState() arbeiten.

Grundsätzlich müssen Sie ein Flag speichern, damit im gespeicherten Instanzpaket ein Fortschrittsdialog ausgeführt wird. Nicht speichere das Fortschrittsdialogobjekt, da dadurch die gesamte Aktivität verloren geht. Damit der Fortschrittsdialog dauerhaft angezeigt wird, speichere ich ihn als schwache Referenz im Anwendungsobjekt. Bei einer Änderung der Ausrichtung oder bei anderen Ereignissen, bei denen die Aktivität angehalten wird (Telefonanruf, Benutzer klickt nach Hause usw.) und dann fortgesetzt wird, wird das alte Dialogfeld geschlossen und in der neu erstellten Aktivität ein neues Dialogfeld erstellt.

Für unbestimmte Fortschrittsdialoge ist dies einfach. Für den Fortschrittsbalkenstil müssen Sie den letzten bekannten Fortschritt in das Bundle einfügen und alle Informationen, die Sie lokal in der Aktivität verwenden, um den Fortschritt zu verfolgen. Beim Wiederherstellen des Fortschritts verwenden Sie diese Informationen, um den Fortschrittsbalken im gleichen Status wie zuvor erneut aufzurufen und dann basierend auf dem aktuellen Status zu aktualisieren.

Zusammenfassend lässt sich sagen, dass Sie durch das Einbinden lang laufender Aufgaben in einen IntentService in Verbindung mit der vernünftigen Verwendung von onSaveInstanceState() den Überblick über Dialoge behalten und diese über die Ereignisse des Aktivitätslebenszyklus hinweg wiederherstellen können. Relevante Teile des Aktivitätscodes sind unten aufgeführt. Sie benötigen auch Logik in Ihrem BroadcastReceiver, um mit Sticky-Absichten angemessen umzugehen, aber das geht über den Rahmen hinaus.

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

Ich bin auf das gleiche Problem gestoßen. Meine Aktivität muss einige Daten von einer URL analysieren und es ist langsam. Dazu erstelle ich einen Thread und zeige dann einen Fortschrittsdialog. Ich lasse den Thread eine Nachricht über Handler an den UI-Thread zurückschicken, wenn er fertig ist. Im Handler.handleMessage, Ich erhalte das Datenobjekt (jetzt bereit) vom Thread und fülle es in der Benutzeroberfläche auf. Es ist also Ihrem Beispiel sehr ähnlich.

Nach vielen Versuchen und Irrtümern scheint ich eine Lösung gefunden zu haben. Zumindest kann ich den Bildschirm jetzt jederzeit drehen, bevor oder nachdem der Thread fertig ist. In allen Tests wird der Dialog ordnungsgemäß geschlossen und alle Verhaltensweisen sind wie erwartet.

Was ich getan habe, ist unten gezeigt. Das Ziel ist, mein Datenmodell (mDataObject) zu füllen und es dann in die Benutzeroberfläche einzufügen. Sollte jederzeit ohne Überraschung eine Drehung des Bildschirms ermöglichen.

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

Das funktioniert bei mir. Ich weiß nicht, ob dies die "richtige" Methode ist, wie sie von Android= - sie behaupten, diese "zerstören/neu erstellen Aktivität während der Bildschirmrotation" macht die Dinge tatsächlich einfacher, also denke ich sollte es nicht sei nicht zu knifflig.

Lassen Sie mich wissen, wenn Sie ein Problem in meinem Code sehen. Wie oben gesagt, weiß ich nicht wirklich, ob es irgendwelche Nebenwirkungen gibt.

27
samsonsu

Das ursprünglich wahrgenommene Problem bestand darin, dass der Code eine Änderung der Bildschirmausrichtung nicht überstehen würde. Anscheinend wurde dies "gelöst", indem das Programm die Änderung der Bildschirmausrichtung selbst handhabte, anstatt das UI-Framework dies tun zu lassen (durch Aufrufen von onDestroy).

Ich würde behaupten, wenn das zugrunde liegende Problem darin besteht, dass das Programm auf Destroy () nicht überlebt, ist die akzeptierte Lösung nur eine Problemumgehung, die das Programm mit ernsthaften anderen Problemen und Schwachstellen belässt. Denken Sie daran, dass das Android Framework ausdrücklich angibt, dass Ihre Aktivität aufgrund von Umständen, die außerhalb Ihrer Kontrolle liegen, fast jederzeit zerstört werden kann. Daher muss Ihre Aktivität in der Lage sein, onDestroy () und höher zu überleben onCreate () ist aus irgendeinem Grund nicht nur eine Änderung der Bildschirmausrichtung.

Wenn Sie akzeptieren, dass Änderungen an der Bildschirmausrichtung vorgenommen werden, um das Problem des OP zu lösen, müssen Sie sicherstellen, dass andere Ursachen für onDestroy () nicht zu demselben Fehler führen. Schaffst du das? Wenn nicht, würde ich fragen, ob die "akzeptierte" Antwort wirklich eine sehr gute ist.

15
gymshoe

Meine Lösung bestand darin, die Klasse ProgressDialog zu erweitern, um meine eigene Klasse MyProgressDialog zu erhalten.
Ich habe die Methoden show() und dismiss() neu definiert, um die Ausrichtung zu sperren, bevor Dialog angezeigt wird, und entsperre sie wieder, wenn Dialog entlassen wird. Wenn also Dialog angezeigt wird und sich die Ausrichtung des Geräts ändert, bleibt die Ausrichtung des Bildschirms so lange erhalten, bis dismiss() aufgerufen wird. Die Ausrichtung des Bildschirms ändert sich dann entsprechend den Sensorwerten/der Ausrichtung des Geräts .

Hier ist mein Code:

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

}
14
Oli

Ich war mit demselben Problem konfrontiert und habe eine Lösung gefunden, die nicht mit dem ProgressDialog funktioniert, und ich erhalte schnellere Ergebnisse.

Ich habe ein Layout erstellt, in dem sich eine Fortschrittsleiste befindet.

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

Führen Sie dann in der onCreate-Methode die folgenden Schritte aus

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

Führen Sie dann die lange Aufgabe in einem Thread aus, und setzen Sie die Inhaltsansicht nach Abschluss von Runnable auf das tatsächliche Layout, das Sie für diese Aktivität verwenden möchten.

Zum Beispiel:

mHandler.post(new Runnable(){

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

Dies ist, was ich getan habe, und ich habe festgestellt, dass es schneller läuft als das Anzeigen des ProgressDialogs und es ist weniger aufdringlich und hat meiner Meinung nach ein besseres Aussehen.

Wenn Sie jedoch den ProgressDialog verwenden möchten, ist diese Antwort nichts für Sie.

8
Pzanno

Ich werde meinen Ansatz zur Behandlung dieses Rotationsproblems einbringen. Dies mag für OP nicht relevant sein, da er AsyncTask nicht verwendet, aber andere werden es vielleicht nützlich finden. Es ist ziemlich einfach, aber es scheint den Job für mich zu machen:

Ich habe eine Anmeldeaktivität mit einer verschachtelten AsyncTask -Klasse namens BackgroundLoginTask.

In meinem BackgroundLoginTask tue ich nichts Ungewöhnliches, außer beim Aufrufen der Entlassung von ProgressDialog eine Nullprüfung hinzuzufügen:

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

Hiermit wird der Fall behandelt, in dem die Hintergrundaufgabe beendet wird, während Activity nicht sichtbar ist und der Fortschrittsdialog daher bereits von der Methode onPause() geschlossen wurde.

Als Nächstes erstelle ich in meiner übergeordneten Klasse Activity globale statische Handles für meine Klasse AsyncTask, und meine Klasse ProgressDialog (die AsyncTask, die verschachtelt ist, kann auf diese zugreifen) Variablen):

private static BackgroundLoginTask backgroundLoginTask;
private static ProgressDialog pleaseWaitDialog;

Dies dient zwei Zwecken: Erstens ermöglicht es meinem Activity, immer auf das AsyncTask -Objekt zuzugreifen, selbst aus einer neuen, nachträglich gedrehten Aktivität. Zweitens erlaubt es meinem BackgroundLoginTask, auch nach einer Drehung auf das ProgressDialog zuzugreifen und es zu schließen.

Als nächstes füge ich dies zu onPause() hinzu, wodurch der Fortschrittsdialog verschwindet, wenn unser Activity den Vordergrund verlässt (um diesen hässlichen Absturz beim Schließen zu verhindern):

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

Schließlich habe ich in meiner onResume() -Methode Folgendes:

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

Auf diese Weise kann Dialog nach der Neuerstellung von Activity erneut angezeigt werden.

Hier ist die gesamte Klasse:

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

    }
}

Ich bin auf keinen Fall ein erfahrener Android Entwickler, also zögern Sie nicht zu kommentieren.

7
Anders

Ich habe eine Lösung dafür gefunden, die ich noch nirgendwo anders gesehen habe. Sie können ein benutzerdefiniertes Anwendungsobjekt verwenden, das weiß, ob Hintergrundaufgaben ausgeführt werden, anstatt dies in der Aktivität zu versuchen, die bei einer Änderung der Ausrichtung zerstört und neu erstellt wird. Ich habe darüber in hier gebloggt.

7
Heikki Toivonen

Ich habe es so gemacht:

    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 {

                }
            }
        }

    }

Sie können auch versuchen und mich wissen lassen, ob es für Sie funktioniert oder nicht

4
Sachin Gurnani

Verschiebe die lange Aufgabe in eine separate Klasse. Implementieren Sie es als Subjekt-Beobachter-Muster. Wenn die Aktivität erstellt wird, registrieren Sie sich und heben Sie beim Schließen die Registrierung für die Taskklasse auf. Die Taskklasse kann AsyncTask verwenden.

4
Vinay

Der Trick besteht darin, den Dialog in AsyncTask während der Ausführung von onPreExecute/onPostExecute wie gewohnt anzuzeigen/zu schließen. Bei einer Änderung der Ausrichtung müssen Sie jedoch eine neue Instanz des Dialogs in der Aktivität erstellen/anzeigen und den Verweis auf die Aufgabe übergeben.

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

Ich habe eine Implementierung, mit der die Aktivität bei einer Änderung der Bildschirmausrichtung zerstört werden kann, aber der Dialog in der neu erstellten Aktivität trotzdem erfolgreich zerstört wird. Ich benutze ...NonConfigurationInstance, um die Hintergrundaufgabe an die neu erstellte Aktivität anzuhängen. Das normale Android Framework erstellt den Dialog selbst neu, dort wird nichts geändert.

Ich habe AsyncTask als Unterklasse zugeordnet und ein Feld für die Aktivität "Eigentümer" und eine Methode zum Aktualisieren dieses Eigentümers hinzugefügt.

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

  ...
}

In meiner Aktivitätsklasse habe ich ein Feld backgroundTask hinzugefügt, das sich auf die 'besessene' Hintergrundaufgabe bezieht, und ich aktualisiere dieses Feld mit onRetainNonConfigurationInstance und 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;
  }
  ...
}

Vorschläge zur weiteren Verbesserung:

  • Löschen Sie die Referenz backgroundTask in der Aktivität, nachdem die Aufgabe beendet ist, um Speicher oder andere damit verbundene Ressourcen freizugeben.
  • Löschen Sie die Referenz ownerActivity in der Hintergrundaufgabe, bevor die Aktivität zerstört wird, falls sie nicht sofort neu erstellt wird.
  • Erstellen Sie eine BackgroundTask -Schnittstelle und/oder -Auflistung, damit verschiedene Aufgabentypen mit derselben Eigentümeraktivität ausgeführt werden können.
2
beetstra

Versucht, die Lösung von jfelectron zu implementieren, da es sich um eine " felsenfeste Lösung für diese Probleme handelt, die der" Android-Methode "der Dinge entspricht", aber es hat einige gedauert Zeit zum Nachschlagen und Zusammenstellen aller genannten Elemente. Endete mit dieser etwas anderen, und ich denke eleganter, hier geposteten Lösung in ihrer Gesamtheit.

Verwendet einen IntentService, der von einer Aktivität ausgelöst wurde, um die Aufgabe mit langer Laufzeit in einem separaten Thread auszuführen. Der Dienst löst festgelegte Broadcast-Absichten für die Aktivität aus, die das Dialogfeld aktualisiert. In der Aktivität werden showDialog (), onCreateDialog () und onPrepareDialog () verwendet, um zu vermeiden, dass persistente Daten im Anwendungsobjekt oder im savedInstanceState-Bundle übergeben werden müssen. Dies sollte unabhängig davon funktionieren, wie Ihre Anwendung unterbrochen wird.

Aktivitätsklasse:

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

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

Manifest-Dateieinträge:

vor dem Bewerbungsabschnitt:

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

innerhalb des Anwendungsbereichs

service Android:name=".MyService"
2
MindSpiker

Wenn Sie einen Hintergrund erstellen Service , der das ganze Heben (TCP-Anforderungen/-Antworten, Entmarshalling) erledigt, können die View und Activity zerstört werden und neu erstellt, ohne dass ein Fenster verloren geht oder Daten verloren gehen. Dies ermöglicht das Android empfohlenes Verhalten, das ist, eine Aktivität bei jeder Konfigurationsänderung zu zerstören (zB für jede Orientierungsänderung).

Es ist etwas komplexer, aber der beste Weg, um Server-Anfragen, Daten vor/nach der Verarbeitung usw. aufzurufen.

Sie können sogar Ihr Service verwenden, um jede Anfrage in eine Warteschlange an einen Server zu stellen, damit Sie diese Dinge einfach und effizient handhaben können.

Der Entwicklerleitfaden enthält ein vollständiges Kapitel zu Services .

2
acardenas89

Wenn Sie zwei Layouts verwalten, sollten alle UI-Threads beendet werden.

Wenn Sie AsynTask verwenden, können Sie die .cancel() -Methode innerhalb der onDestroy() -Methode der aktuellen Aktivität aufrufen.

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

Weitere Informationen zu AsyncTask finden Sie im Abschnitt "Abbrechen einer Aufgabe" unter hier .

Update: Es wurde eine Bedingung hinzugefügt, um den Status zu überprüfen, da der Vorgang nur abgebrochen werden kann, wenn er ausgeführt wird. Beachten Sie auch, dass die AsyncTask nur einmal ausgeführt werden kann.

2
Vikas

Dies ist meine vorgeschlagene Lösung:

  • Verschieben Sie die AsyncTask oder den Thread auf ein beibehaltenes Fragment, wie beschrieben hier . Ich halte es für eine gute Praxis, alle Netzwerkanrufe in Fragmente zu verschieben. Wenn Sie bereits Fragmente verwenden, kann eines davon für die Aufrufe verantwortlich gemacht werden. Andernfalls können Sie ein Fragment nur für die Anforderung erstellen, wie im verknüpften Artikel vorgeschlagen.
  • Das Fragment verwendet eine Listener-Schnittstelle, um die Beendigung/den Fehler der Aufgabe anzuzeigen. Sie müssen sich dort keine Sorgen um Orientierungsänderungen machen. Das Fragment hat immer den richtigen Link zur aktuellen Aktivität und der Fortschrittsdialog kann sicher fortgesetzt werden.
  • Machen Sie Ihren Fortschrittsdialog zu einem Mitglied Ihrer Klasse. Tatsächlich sollten Sie das für alle Dialoge tun. Bei der Methode onPause sollten Sie sie schließen, da sonst ein Fenster für die Konfigurationsänderung verloren geht. Der beschäftigte Zustand sollte durch das Fragment gehalten werden. Wenn das Fragment an die Aktivität angehängt ist, können Sie den Fortschrittsdialog erneut aufrufen, wenn der Anruf noch ausgeführt wird. Zu diesem Zweck kann eine void showProgressDialog() -Methode zur Listener-Schnittstelle für Fragmentaktivitäten hinzugefügt werden.
2
kgiannakakis

Ich bin ein Neuling in Android und ich habe es versucht und es hat funktioniert.

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

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

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

Ich habe alles versucht. Verbrachte Tage experimentieren. Ich wollte die Aktivität nicht daran hindern, sich zu drehen. Mein Szenario war:

  1. Ein Fortschrittsdialog, der dem Benutzer dynamische Informationen anzeigt. ZB "Verbindung zum Server wird hergestellt ...", "Daten werden heruntergeladen ..." usw.
  2. Ein Thread, der die schweren Dinge erledigt und den Dialog aktualisiert
  3. Aktualisierung der Benutzeroberfläche mit den Ergebnissen am Ende.

Das Problem war, dass beim Drehen des Bildschirms jede Lösung des Buches fehlschlug. Auch mit der AsyncTask-Klasse, die die richtige Android Art ist, mit diesen Situationen umzugehen. Beim Drehen des Bildschirms wird der aktuelle Kontext, mit dem der Start-Thread arbeitet, nicht mehr angezeigt und der angezeigte Dialog wird durcheinander gebracht. Das Problem war immer der Dialog, egal wie viele Tricks ich dem Code hinzugefügt habe (neue Kontexte an laufende Threads übergeben, Thread-Status durch Rotationen beibehalten usw.). Die Codekomplexität am Ende war immer riesig und es gab immer etwas, was schief gehen konnte.

Die einzige Lösung, die für mich funktionierte, war der Trick Aktivität/Dialog. Es ist einfach und genial und alles ist rotationssicher:

  1. Anstatt ein Dialogfeld zu erstellen und es anzuzeigen, erstellen Sie eine Aktivität, die mit Android im Manifest festgelegt wurde: theme = "@ Android: style/Theme.Dialog". Es sieht also aus wie ein Dialog.

  2. Ersetzen Sie showDialog (DIALOG_ID) durch startActivityForResult (yourActivityDialog, yourCode);

  3. Verwenden Sie onActivityResult in der aufrufenden Aktivität, um die Ergebnisse des ausgeführten Threads (auch die Fehler) abzurufen und die Benutzeroberfläche zu aktualisieren.

  4. Verwenden Sie in Ihrem 'ActivityDialog' Threads oder AsyncTask, um lange Aufgaben auszuführen, und onRetainNonConfigurationInstance, um den Status "dialog" beim Drehen des Bildschirms zu speichern.

Das ist schnell und funktioniert gut. Ich verwende immer noch Dialoge für andere Aufgaben und die AsyncTask für etwas, das keinen konstanten Dialog auf dem Bildschirm erfordert. Aber mit diesem Szenario gehe ich immer zum Aktivitäts-/Dialogmuster.

Ich habe es nicht ausprobiert, aber es ist sogar möglich, das Drehen dieser Aktivität/dieses Dialogfelds zu blockieren, wenn der Thread ausgeführt wird, wodurch die Dinge beschleunigt werden und die aufrufende Aktivität rotieren kann.

1
Rui

Heutzutage gibt es eine viel eindeutigere Art, mit solchen Problemen umzugehen. Der typische Ansatz ist:

1. Stellen Sie sicher, dass Ihre Daten ordnungsgemäß von der Benutzeroberfläche getrennt sind:

Alles, was ein Hintergrundprozess ist, sollte sich in einem beibehaltenen Fragment befinden (stellen Sie dies mit Fragment.setRetainInstance() ein. Dies wird zu Ihrem "dauerhaften Datenspeicher", in dem alle Daten, die Sie beibehalten möchten, aufbewahrt werden Während des Ausrichtungsänderungsereignisses ist dieses Fragment in seinem ursprünglichen Zustand weiterhin über einen FragmentManager.findFragmentByTag() -Aufruf zugänglich (wenn Sie es erstellen, sollten Sie ihm ein Tag geben keine ID , da sie nicht an ein View angehängt ist).

Informationen dazu, wie Sie dies richtig machen und warum dies die beste Option ist, finden Sie im entwickelten Handbuch mgang mit Laufzeitänderungen .

2. Stellen Sie sicher, dass Sie eine korrekte und sichere Verbindung zwischen den Hintergrundprozessen und Ihrer Benutzeroberfläche herstellen:

Sie müssen Ihren Verknüpfungsprozess umkehren . Im Moment hängt sich Ihr Hintergrundprozess an einen View an - stattdessen sollte sich Ihr View an den Hintergrundprozess anhängen. Es macht doch mehr Sinn oder? Die Aktion von View ist vom Hintergrundprozess abhängig, wohingegen der Hintergrundprozess nicht vom View abhängig ist. Dies bedeutet, dass die Verknüpfung zu einer Standardschnittstelle von Listener geändert wird. Sagen Sie, Ihr Prozess (unabhängig von der Klasse - ob es sich um einen AsyncTask, Runnable oder was auch immer handelt) definiert einen OnProcessFinishedListener. Wenn der Prozess abgeschlossen ist, sollte er diesen Listener aufrufen, wenn er ausgeführt wird existiert.

Dies Antwort ist eine kurze Beschreibung, wie man benutzerdefinierte Listener ausführt.

3. Binden Sie Ihre Benutzeroberfläche bei jeder Erstellung der Benutzeroberfläche (einschließlich Änderungen der Ausrichtung) in den Datenprozess ein:

Jetzt müssen Sie sich darum kümmern, die Hintergrundaufgabe mit Ihrer aktuellen View -Struktur zu verknüpfen. Wenn Sie Ihre Orientierungsänderungen richtig handhaben (nicht die configChanges, die immer von Hackern empfohlen werden), wird dies Ihr Dialog tun vom System neu erstellt werden. Dies ist wichtig. Dies bedeutet, dass bei einer Änderung der Ausrichtung alle Lebenszyklusmethoden Ihres Dialog abgerufen werden. In jeder dieser Methoden (onCreateDialog ist normalerweise ein guter Ort) könnten Sie einen Aufruf wie den folgenden ausführen:

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

Lesen Sie den Fragment-Lebenszyklus , um zu entscheiden, wo die Einstellung des Listeners am besten zu Ihrer individuellen Implementierung passt.

Dies ist ein allgemeiner Ansatz, um eine robuste und vollständige Lösung für das in dieser Frage gestellte allgemeine Problem bereitzustellen. Abhängig von Ihrem individuellen Szenario fehlen in dieser Antwort wahrscheinlich ein paar kleinere Teile, aber dies ist im Allgemeinen der korrekteste Ansatz für den richtigen Umgang mit Ereignissen, bei denen sich die Ausrichtung ändert.

1
B T

Dies ist eine sehr alte Frage, die aus irgendeinem Grund in der Seitenleiste auftauchte.

Wenn die Hintergrundaufgabe nur überleben muss, während sich die Aktivität im Vordergrund befindet, besteht die "neue" Lösung darin, den Hintergrundthread (oder vorzugsweise AsyncTask) in einem zu hosten. beibehaltenes Fragment , wie in diesem Entwicklerhandbuch und zahlreiche Fragen und Antworten beschrieben.

Ein beibehaltenes Fragment bleibt erhalten, wenn die Aktivität für eine Konfigurationsänderung zerstört wird, aber nicht wenn die Aktivität im Hintergrund oder im Backstack zerstört wird. Daher sollte die Hintergrundaufgabe immer noch unterbrochen werden, wenn isChangingConfigurations() in onPause() falsch ist.

1
Kevin Krumwiede

Ich sah mich der gleichen Situation gegenüber. Ich habe nur eine Instanz für meinen Fortschrittsdialog in der gesamten Anwendung erhalten.

Zuerst habe ich eine DialogSingleton-Klasse erstellt, um nur eine Instanz zu erhalten (Singleton-Muster).

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

Wie ich in dieser Klasse zeige, habe ich den Fortschrittsdialog als Attribut. Jedes Mal, wenn ich einen Fortschrittsdialog anzeigen muss, erhalte ich die eindeutige Instanz und erstelle einen neuen Fortschrittsdialog.

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

Wenn ich mit der Hintergrundaufgabe fertig bin, rufe ich die eindeutige Instanz erneut auf und schließe ihren Dialog ab.

DialogSingleton.GetInstance().DialogDismiss(this);

Ich speichere den Status der Hintergrundaufgabe in meinen gemeinsamen Einstellungen. Wenn ich den Bildschirm drehe, frage ich, ob für diese Aktivität eine Aufgabe ausgeführt wird: (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).

Wenn ich eine Hintergrundaufgabe starte:

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

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

Wenn ich mit dem Ausführen einer Hintergrundaufgabe fertig bin:

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

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

Ich hoffe, es hilft.

1
deinier

ich habe eine einfachere Lösung gefunden, um mit Fäden umzugehen, wenn sich die Ausrichtung ändert. Sie können einfach einen statischen Verweis auf Ihre Aktivität/Ihr Fragment behalten und überprüfen, ob es null ist, bevor Sie auf die Benutzeroberfläche einwirken. Ich schlage vor, auch einen Versuch zu fangen:

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


}
0
sagits

Scheint viel zu 'schnell und dreckig', um wahr zu sein. Bitte weisen Sie auf die Mängel hin, aber was ich gefunden habe, hat funktioniert, war ...

Innerhalb der onPostExecute-Methode meiner AsyncTask habe ich einfach die '.dismiss' für den Fortschrittsdialog in einen try/catch-Block (mit einem leeren catch) eingebunden und die ausgelöste Ausnahme einfach ignoriert. Scheint falsch zu sein, scheint aber keine negativen Auswirkungen zu haben (zumindest für das, was ich später tue, ist es, eine weitere Aktivität zu starten, die als Ergebnis meiner langen Abfrage als Extra übergeben wird).

0
Simon

Die einfachste und flexibelste Lösung ist die Verwendung eines AsyncTask mit einem statischen Verweis auf ProgressBar . Dies bietet eine gekapselte und somit wiederverwendbare Lösung für Probleme mit Orientierungsänderungen. Diese Lösung hat mir bei verschiedenen asynchronen Aufgaben geholfen, einschließlich Internet-Downloads, Kommunikation mit Services und Dateisystem-Scans. Die Lösung wurde auf mehreren Android Versionen und Telefonmodellen getestet. Eine vollständige Demo finden Sie hier mit besonderem Interesse an DownloadFile.Java =

Ich präsentiere das Folgende als Konzeptbeispiel

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

Die Verwendung in einer Android Aktivität ist einfach

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

Wenn Sie Schwierigkeiten haben, Orientierungsänderungsereignisse in einem Dialogfeld zu erkennen NABHÄNGIG VON EINER AKTIVITÄTSREFERENZ, funktioniert diese Methode aufregend gut. Ich benutze dies, weil ich meine eigene Dialogklasse habe, die in mehreren verschiedenen Aktivitäten angezeigt werden kann, sodass ich nicht immer weiß, in welcher Aktivität sie angezeigt wird. Bei dieser Methode müssen Sie das Android-Manifest nicht ändern. und Sie brauchen kein benutzerdefiniertes Dialogfeld (wie ich). Sie benötigen jedoch eine benutzerdefinierte Inhaltsansicht, damit Sie die Ausrichtungsänderungen in dieser bestimmten Ansicht erkennen können. Hier ist mein Beispiel:

Konfiguration

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

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

        //DO SOMETHING HERE!! :D
    }
}

Implementierung 1 - Dialog

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

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

Implementierung 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();
0
kpninja12