Frage

Ich habe ein Asynctask -Objekt, das mit der Ausführung beginnt, wenn die Aktivität erstellt wird und Dinge im Hintergrund ausführt (Downloads bis zu 100 Bilder). Alles funktioniert gut, aber es gibt dieses besondere Verhalten, das ich nicht verstehen kann.

Für EG: Wenn sich die Ausrichtung des Android -Bildschirms ändert, wird die Aktivität erneut zerstört und wieder erstellt. Daher überschreibe ich die OnretainnonConfigurationInstance () -Methode () und speichere alle heruntergeladenen Daten, die in der Asynctask ausgeführt wurden. Mein Ziel, dies zu tun, ist es, nicht jedes Mal einen Asynctask-Lauf zu erzielen, wenn die Aktivität während der Orientierungsänderungen zerstört wird, aber wie ich in meinen Protokollen sehen kann, wird der vorherige Asynctask immer noch ausgeführt. (Die Daten werden jedoch korrekt gespeichert)

Ich habe sogar versucht, die Asynctask in der OnDestroy () -Methode der Aktivität zu stornieren, aber die Protokolle zeigen immer noch Asynctask als Laufen.

Dies ist wirklich seltsames Verhalten und wäre wirklich dankbar, wenn mir jemand die richtige Prozedur sagen kann, um die Asynctask zu stoppen/abzubrechen.

Vielen Dank

War es hilfreich?

Lösung

Die Antwort von @romain Guy ist korrekt. Trotzdem möchte ich eine Informationen ergänzen und einem oder 2 Zeiger einen Zeiger geben, der für langlebige Asynctask verwendet werden kann, und noch mehr für netzwerkorientierte Asynctasks.

Asynctasks wurden für Sachen im Hintergrund entwickelt. Und ja, Sie können es mit dem stoppen cancel Methode. Wenn Sie Sachen aus dem Internet herunterladen, schlage ich Ihnen dringend vor Kümmere dich um deinen Thread, wenn es der IO -Blockierungszustand ist. Sie sollten Ihren Download wie folgt organisieren:

public void download() {
    //get the InputStream from HttpUrlConnection or any other
    //network related stuff
    while( inputStream.read(buffer) != -1 && !Thread.interrupted() ) {
      //copy data to your destination, a file for instance
    }
    //close the stream and other resources
}

Verwendung der Thread.interrupted Flag hilft Ihrem Thread, einen blockierenden IO -Zustand richtig zu beenden. Ihr Thread reagiert stärker auf eine Anrufung des cancel Methode.

Asynctask -Designfehler

Aber wenn Ihr Asynctask zu lange dauert, werden Sie 2 verschiedene Probleme haben:

  1. Aktivitäten sind schlecht mit dem Aktivitätslebenszyklus verbunden und Sie werden das Ergebnis Ihrer Asynctask nicht erhalten, wenn Ihre Aktivität stirbt. In der Tat, ja, Sie können, aber es wird der grobe Weg sein.
  2. Asynctask ist nicht sehr gut dokumentiert. Eine naive, wenn auch intuitive Implementierung und Verwendung eines Asynctasks kann schnell zu Speicherlecks führen.

Robospiz, Die Bibliothek, die ich vorstellen möchte, verwendet einen Hintergrunddienst, um diese Art von Anfragen auszuführen. Es wurde für Netzwerkanfragen entwickelt. Es bietet zusätzliche Funktionen wie das automatische Abschnitt der Anfragenergebnisse.

Hier ist der Grund, warum Asynctasks für langjährige Aufgaben schlecht sind. Das folgende Denken ist eine Anpassung aus Exerpts von Robospice -Motivationen : Die App, die erklärt, warum die Verwendung von Robospize einen Bedarf auf der Android -Plattform erfüllt.

Der Asynctask- und Aktivitätslebenszyklus

Asynctasks folgen nicht dem Lebenszyklus der Aktivitätsinstanzen. Wenn Sie eine Asynctask in einer Aktivität starten und das Gerät drehen, wird die Aktivität zerstört und eine neue Instanz erstellt. Aber der Asynctask wird nicht sterben. Es wird weiter leben, bis es abgeschlossen ist.

Und wenn es abgeschlossen ist, wird die Asynctask die Benutzeroberfläche der neuen Aktivität nicht aktualisieren. In der Tat aktualisiert es die frühere Instanz der Aktivität, die nicht mehr angezeigt wird. Dies kann zu einer Ausnahme des Typs java.lang.IillegalargumentException: Ansicht nicht an den Fenstermanager beigefügt, wenn Sie beispielsweise FindViewById verwenden, um eine Ansicht innerhalb der Aktivität abzurufen.

Problemlautproblem

Es ist sehr bequem, Asynctasks als innere Klassen Ihrer Aktivitäten zu erstellen. Da der Asynctask die Ansichten der Aktivität manipulieren muss, wenn die Aufgabe vollständig oder in Arbeit ist, erscheint die Verwendung einer inneren Klasse der Aktivität bequem: Innenklassen können direkt auf jedes Feld der äußeren Klasse zugreifen.

Dennoch bedeutet dies, dass die innere Klasse eine unsichtbare Referenz auf die Instanz der äußeren Klasse hat: die Aktivität.

Auf lange Sicht erzeugt dies ein Speicherleck: Wenn die Asynctask lange dauert, hält sie die Aktivität "lebendig", während Android es gerne loswerden möchte, da sie nicht mehr angezeigt werden kann. Die Aktivität kann nicht Müll gesammelt werden, und das ist ein zentraler Mechanismus für Android, um Ressourcen auf dem Gerät zu erhalten.

Der Fortschritt Ihrer Aufgabe wird verloren gehen

Sie können einige Problemumgehungen verwenden, um einen langjährigen Asynctask zu erstellen und ihren Lebenszyklus entsprechend dem Lebenszyklus der Aktivität zu verwalten. Du kannst entweder Stornieren Sie die Asynctask in der OnStop -Methode Ihrer Aktivität Oder Sie können Ihre asynchronisierte Aufgabe beenden lassen und ihren Fortschritt nicht verlieren und Geben Sie es mit der nächsten Instanz Ihrer Aktivität neu an.

Dies ist möglich und wir zeigen, wie in Robopspice -Motivationen, aber er wird kompliziert und der Code ist nicht wirklich generisch. Darüber hinaus verlieren Sie immer noch den Fortschritt Ihrer Aufgabe, wenn der Benutzer die Aktivität verlässt und zurückkommt. Das gleiche Problem erscheint bei Ladern, obwohl es ein einfacheres Äquivalent zum Asynctask wäre, wobei oben erwähnt wird.

Nutzung eines Android -Dienstes

Die beste Option ist die Verwendung eines Dienstes, um Ihre lang laufenden Hintergrundaufgaben auszuführen. Und genau das ist die von Robospiz vorgeschlagene Lösung. Auch hier ist es für die Vernetzung konzipiert, könnte aber auf nicht-network-verwandte Dinge ausgedehnt werden. Diese Bibliothek hat eine große Anzahl von Funktionen.

Dank eines können Sie sogar in weniger als 30 Sekunden eine Vorstellung davon bekommen Infografiken.


Es ist wirklich eine sehr sehr schlechte Idee, Asynctasks für langjährige Operationen zu verwenden. Trotzdem sind sie für kurze Lebende in Ordnung, z. B. für die Aktualisierung einer Ansicht nach 1 oder 2 Sekunden.

Ich ermutige Sie, die herunterzuladen Robospice Motivations App, Es erklärt dies wirklich eingehend und bietet Beispiele und Demonstrationen der verschiedenen Möglichkeiten, einige netzwerkbezogene Dinge zu erledigen.


Wenn Sie nach einer Alternative zu Robospize für nicht netzwerkbezogene Aufgaben suchen (z. B. ohne Zwischenspeicherung), können Sie sich auch ansehen Band.

Andere Tipps

In der Regel können Sie in der Regel List-Instanzen und Daten mit etwas Ähnlichem angeben, das unten ähnlich ist: generasacodicetagpre.

Da die Umfrage jedoch mehr "SPECIAL" ist, wenn Sie möchten, dass die eigentlichen Fragen Spalten auf Ihrer Liste sind - und am meisten undokumentiert (in Bezug auf Feldtypen - Liste auf MSDN verfügbar bei http://msdn.microsoft.com/de-us/library/ms948164.aspx )

Sie müssen also tatsächlich die Fragen in den Spalten definieren, während sich die tatsächlichen Antworten im Element "Daten" befinden. Da Fragen als benutzerdefinierte Felder mit einem bestimmten Namen definiert sind, muss das Fragefeld enthalten, und dieses Feld muss mit dem Felddefinitionsschema übereinstimmen - viel Arbeit! Eine Alternative könnte sein, die Liste als STP zu exportieren und einen Blick in das Manifest zu sehen.

Versuchen Sie etwas wie folgt (nicht in der Lage, das Äquivalent für eine Likert-Skala in der Lage zu finden, können jedoch einfache Fragen als normale Spalten sein). Auch für Separatorsäulen habe ich festgestellt, dass ein Feld des Typs "PageSeparator" grundsätzlich einen Seitenumbruch in Ihre Umfrage einfügt: generasacodicetagpre.

Das Folgende löst Ihr Problem nicht, verhindert es jedoch: In der App Manifest tun Sie Folgendes:

    <activity
        android:name=".(your activity name)"
        android:label="@string/app_name"
        android:configChanges="orientation|keyboardHidden|screenSize" > //this line here
    </activity>

Wenn Sie dies hinzufügen, laden Sie Ihre Aktivität nicht bei der Änderung der Konfiguration neu. Wenn Sie bei Änderungen der Orientierung einige Änderungen vornehmen möchten, überschreiben Sie nur die folgende Methode der Aktivität:

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

        //your code here
    }

Die Aktivität wird bei der Orientierungsänderung nachgebildet, ja, das ist wahr. Sie können Sie jedoch weiterhin Asynctask fortsetzen, wenn dieses Ereignis stattfindet.

Sie überprüfen es an

@Override
protected void onCreate(Bundle savedInstanceState) { 
     if ( savedInstanceState == null ) {
           startAsyncTask()
     } else {
           // ** Do Nothing async task will just continue.
     }
}

-Prost

Von dem MVC Standpunkt, Aktivität ist die Regler; es ist falsch für die Regler Operationen durchführen, die die überleben Aussicht (Abgeleitet von Android.View.View verwenden Sie normalerweise nur die vorhandenen Klassen.) Daher sollte es das sein ModellDie Verantwortung, Asynctasks zu starten.

Wenn sich Asynctask nicht im Thread -Pool (parallele Verarbeitung) befindet, können Sie nicht aufhören, Asynctask auszuführen, da er bereits von der CPU ausgeführt wird und der Befehl Stop oder Abbrechen nach der kostenlosen CPU ausgeführt wird (CPU ist mit Asynctask). Es ist jedoch im Thread -Pool, die Aufgaben werden in der Warteschlange und werden einzeln ausgeführt. Wenn Ihr Cancer -Befehlsbefehl während der Ausführung einer asynchronen Aufgabe in die Warteschlange gestellt wird, kann dies Ihre asynchronisierende Aufgabe stoppen.

Sie können verwenden class MagicAppRestart aus dieser Beitragzu Töte den Prozess zusammen mit allen Asynctasks; Android wird den Aktivitätsstapel wiederherstellen (der Benutzer erwähnt nichts). Es ist wichtig zu beachten onPause(); laut dem Android App Lifecycle Logic, Ihre Bewerbung muss ohnehin auf eine solche Kündigung bereit sein.

Ich habe es versucht und es scheint zu funktionieren. In dem Moment, in dem ich "mehr zivilisierte" Methoden wie schwache Referenzen aus der Anwendungsklasse anwenden möchte (meine Asynctasks sind ziemlich kurzlebig und hoffentlich nicht so viel Gedächtnisverbrauch).

Hier ist ein Code, mit dem Sie spielen können:

Magic Apprestart.java

package com.xyz;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;

/** This activity shows nothing; instead, it restarts the android process */
public class MagicAppRestart extends Activity {
    // Do not forget to add it to AndroidManifest.xml
    // <activity android:name="your.package.name.MagicAppRestart"/>
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        System.exit(0);
    }
    public static void doRestart(Activity anyActivity) {
        anyActivity.startActivity(new Intent(anyActivity.getApplicationContext(), MagicAppRestart.class));
    }
}

Der Rest ist das, was Eclipse für ein neues Android -Projekt erstellt hat com.xyz.asyncKaskTestactivity:

AsyncKaskTestactivity.java

package com.xyz;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

public class AsyncTaskTestActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        Log.d("~~~~","~~~onCreate ~~~ "+this);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
    public void onStartButton(View view) {
        Log.d("~~~~","~~~onStartButton {");
        class MyTask extends AsyncTask<Void, Void, Void> {

            @Override
            protected Void doInBackground(Void... params) {
                // TODO Auto-generated method stub
                Log.d("~~~~","~~~doInBackground started");
                try {
                    for (int i=0; i<10; i++) {
                        Log.d("~~~~","~~~sleep#"+i);
                        Thread.sleep(200);
                    }
                    Log.d("~~~~","~~~sleeping over");
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                Log.d("~~~~","~~~doInBackground ended");
                return null;
            }
            @Override
            protected void onPostExecute(Void result) {
                super.onPostExecute(result);
                taskDone();
            }
        }
        MyTask task = new MyTask();
        task.execute(null);
        Log.d("~~~~","~~~onStartButton }");
    }
    private void taskDone() {
        Log.d("~~~~","\n\n~~~taskDone ~~~ "+this+"\n\n");
    }
    public void onStopButton(View view) {
        Log.d("~~~~","~~~onStopButton {");
        MagicAppRestart.doRestart(this);
        Log.d("~~~~","~~~onStopButton }");
    }
    public void onPause() {   Log.d("~~~~","~~~onPause ~~~ "+this);   super.onPause(); }
    public void onStop() {    Log.d("~~~~","~~~onStop ~~~ "+this);    super.onPause(); }
    public void onDestroy() { Log.d("~~~~","~~~onDestroy ~~~ "+this); super.onDestroy(); }
}

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <Button android:text="Start" android:onClick="onStartButton" android:layout_width="fill_parent" android:layout_height="wrap_content"/>
    <Button android:text="Stop" android:onClick="onStopButton" android:layout_width="fill_parent" android:layout_height="wrap_content"/>
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />

</LinearLayout>

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.xyz"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-sdk android:minSdkVersion="7" />
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".AsyncTaskTestActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".MagicAppRestart"/>
    </application>
</manifest>

und ein relevanter Teil der Protokolle (beachten Sie das nur onPause wird genannt):

D/~~~~    (13667): ~~~onStartButton {
D/~~~~    (13667): ~~~onStartButton }
D/~~~~    (13667): ~~~doInBackground started
D/~~~~    (13667): ~~~sleep#0
D/~~~~    (13667): ~~~sleep#1
D/~~~~    (13667): ~~~sleep#2
D/~~~~    (13667): ~~~sleep#3
D/~~~~    (13667): ~~~sleep#4
D/~~~~    (13667): ~~~sleep#5
D/~~~~    (13667): ~~~sleep#6
D/~~~~    (13667): ~~~sleep#7
D/~~~~    (13667): ~~~sleep#8
D/~~~~    (13667): ~~~sleep#9
D/~~~~    (13667): ~~~sleeping over
D/~~~~    (13667): ~~~doInBackground ended
D/~~~~    (13667): 
D/~~~~    (13667): 
D/~~~~    (13667): ~~~taskDone ~~~ com.xyz.AsyncTaskTestActivity@40516988
D/~~~~    (13667): 




D/~~~~    (13667): ~~~onStartButton {
D/~~~~    (13667): ~~~onStartButton }
D/~~~~    (13667): ~~~doInBackground started
D/~~~~    (13667): ~~~sleep#0
D/~~~~    (13667): ~~~sleep#1
D/~~~~    (13667): ~~~sleep#2
D/~~~~    (13667): ~~~sleep#3
D/~~~~    (13667): ~~~sleep#4
D/~~~~    (13667): ~~~sleep#5
D/~~~~    (13667): ~~~onStopButton {
I/ActivityManager(   81): Starting: Intent { cmp=com.xyz/.MagicAppRestart } from pid 13667
D/~~~~    (13667): ~~~onStopButton }
D/~~~~    (13667): ~~~onPause ~~~ com.xyz.AsyncTaskTestActivity@40516988
I/ActivityManager(   81): Process com.xyz (pid 13667) has died.
I/WindowManager(   81): WIN DEATH: Window{4073ceb8 com.xyz/com.xyz.AsyncTaskTestActivity paused=false}
I/ActivityManager(   81): Start proc com.xyz for activity com.xyz/.AsyncTaskTestActivity: pid=13698 uid=10101 gids={}
I/ActivityManager(   81): Displayed com.xyz/.AsyncTaskTestActivity: +44ms (total +65ms)
D/~~~~    (13698): ~~~onCreate ~~~ com.xyz.AsyncTaskTestActivity@40517238
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top