Question

Je travaille avec AsyncTasks dans Android et je traite un problème.

Prenons un exemple simple, une activité avec un AsyncTask. La tâche sur l'arrière-plan ne fait pas rien de spectaculaire, il dort juste 8 secondes.

A la fin de la AsyncTask dans la méthode onPostExecute () Je suis en train juste un état de visibilité du bouton pour View.VISIBLE, pour vérifier mes résultats.

Maintenant, cela fonctionne très bien jusqu'à ce que l'utilisateur décide de changer son orientation de téléphones alors que le AsyncTask fonctionne (dans les 8 seconde fenêtre de sommeil).

Je comprends le cycle de vie d'activité Android et je sais que l'activité est détruite et recréée.

est là le problème. Le AsyncTask fait référence à un bouton et tient apparemment une référence au contexte qui a commencé la AsyncTask en premier lieu.

Je me attends, que ce vieux contexte (car l'utilisateur a provoqué un changement d'orientation) soit devenue nulle et AsyncTask de jeter un NPE pour la référence au bouton, il tente de rendre visible.

Au lieu de cela, pas NPE est jeté, la AsyncTask pense que la référence du bouton est non nul, il fixe à visible. Le résultat? Rien ne se passe sur l'écran!

Mise à jour: Je l'ai abordé cela en gardant un WeakReference à l'activité et la commutation lors d'un changement de configuration se produit. Ceci est lourd.

Voici le code:

public class Main extends Activity {

    private Button mButton = null;
    private Button mTestButton = null;

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

        mButton = (Button) findViewById(R.id.btnStart);
        mButton.setOnClickListener(new OnClickListener () {
            @Override
            public void onClick(View v) {
                new taskDoSomething().execute(0l);
            }
        });
        mTestButton = (Button) findViewById(R.id.btnTest);   
    }

    private class TaskDoSomething extends AsyncTask<Long, Integer, Integer> 
    {
        @Override
        protected Integer doInBackground(Long... params) {
            Log.i("LOGGER", "Starting...");
            try {
                Thread.sleep(8000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 0;
        }

        @Override
        protected void onPostExecute(Integer result) {
            Log.i("LOGGER", "...Done");
            mTestButton.setVisibility(View.VISIBLE);
        }
    }
}

Essayez d'exécuter et pendant que le AsyncTask travaille changer votre orientation du téléphone.

Était-ce utile?

La solution

AsyncTask n'a pas été conçu pour être réutilisé une fois que l'activité a été démolie et redémarré. L'objet gestionnaire interne devient obsolète, comme vous l'avez dit. Dans les étagères par exemple Romain Guy, il simple, tout de AsyncTask annule en cours d'exécution, puis redémarre de nouveaux changements post-orientation.

Il est possible de remettre votre fil à la nouvelle activité, mais il ajoute beaucoup de plomberie. Il n'y a pas d'accord général sur la façon de le faire, mais vous pouvez lire sur ma méthode ici: http://foo.jasonhudgins.com/2010/03/simple-progressbar-tutorial.html

Autres conseils

Si vous avez seulement besoin d'un contexte et ne l'utiliser pour des trucs ui vous pouvez simplement passer le ApplicationContext à votre besoin AsyncTask.You souvent le contexte des ressources du système, par exemple.

Ne pas essayer de mettre à jour l'interface utilisateur d'un AsyncTask et essayer d'éviter la manipulation de configuration vous change car il peut être gênant. Afin de mettre à jour l'interface utilisateur, vous pouvez enregistrer un récepteur de diffusion et envoyer une diffusion.

Vous devriez également avoir la AsyncTask en tant que classe publique distincte de l'activité tel que mentionné ci-dessus, il facilite le test beaucoup plus facile. Malheureusement, la programmation Android renforce souvent les mauvaises pratiques et les exemples ne sont pas officielles aide.

Ceci est le genre de chose qui me conduit à toujours empêcher mon activité de destruction / recréée sur le changement d'orientation.

Pour ce faire, ajoutez ceci à votre tag <Activity> dans votre fichier manifeste:

android:configChanges="orientation|keyboardHidden" 

Et passer outre onConfigurationChanged dans votre classe d'activité:

@Override
public void onConfigurationChanged(final Configuration newConfig)
{
    // Ignore orientation change to keep activity from restarting
    super.onConfigurationChanged(newConfig);
}

Pour éviter cela, vous pouvez utiliser la réponse Givin ici: https://stackoverflow.com/a/2124731/327011

Mais si vous avez besoin de détruire l'activité (mises en page différentes pour portrait et paysage), vous pouvez faire la AsyncTask une classe publique (Lisez ici pourquoi il ne devrait pas être privé Android: recommandations de AsyncTask: classe privée ou public class ) puis créer un setActivity de méthode pour définir la référence au courant activité chaque fois qu'il est détruit / créé.

Vous pouvez voir un exemple ici: Android AsyncTask en classe externe

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top