2017-09-14 5 views
0

J'essaie de mettre à jour mon application pour gérer les changements de configuration (en particulier le changement d'écran) manuellement.Android: questions sur la gestion manuelle des modifications de configuration pendant l'exécution du thread

J'ai quelques questions sur ce qui se passe lorsque des changements surviennent au cours d'une exécution Thread.

J'ai créé une classe abstraite que j'appelle ThreadTask et qui utilise Threads and Handlers dans le looper du thread principal pour envoyer des mises à jour au thread principal. C'est mon implémentation d'AsyncTask mais avec des threads, je préfère utiliser AsyncTask car j'ai plus de contrôle sur elle.

Il dispose également de deux méthodes pour enregistrer un observateur aux événements ci-dessus, il utilise cette interface:

public interface TaskObserver { 
    void pre_execute(); 
    void on_progress(ProgressData progress); 
    void finished(Object result); 
    void cancelled(Object result); 
} 

Les membres abstraits que la sous-classe doit mettre en œuvre sont:

abstract Object do_in_background(); 

et du béton les membres sont:

synchronized void add_observer(TaskObserver observer){ 
    _observers.add(observer); 
} 

synchronized void remove_observer(TaskObserver observer){ 
    _observers.remove(observer); 
} 

synchronized void post_execute(Object result) { 
    int observers = _observers.size(); 
    for (int idx = 0; idx < observers; idx++) { 
     _observers.get(idx).finished(result); 
    } 
} 
///plus all the other methods of the interface 

Donc quand j'implémente une classe de béton ça irait mething comme ceci:

public class MyThreadTask extends ThreadTask{ 

    @Override 
    Object do_in_background() { 
     progress.primary_max = 5; 
     for (int cur = 0 ; cur < 5 ; cur++) { 
      progress.primary = cur; 
      publish(); 
      Thread.Sleep(3000); 
     } 
    } 

} 

et je mis à jour l'activité qui appelle cela comme ceci:

static final string TAG ="my_main_activity"; 

MyDataFragment _data; //this is a datafragment, a fragment with retaininstancestate , and a public `MyThreadTask task` variable 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    if (_data == null) { 
     _data = (MyDataFragment)getFragmentManager().findFragmentByTag(TAG + "_data"); 
     if (_data == null) { 
      _data = new MyDataFragment(); 
      getFragmentManager().beginTransaction().add(_data, TAG + "_data").commit(); 
     } 
    } 
    if (_data.task != null) { 
     _data.task.register(this); 
    } 
} 

@Override 
protected void onDestroy() { 
    super.onDestroy(); 
    if (_data.task != null) { 
     _data.task.remove(this); 
    } 
} 

ce fait en sorte que je l'ai toujours une référence au fil correct

Quand je veux commencer la tâche Je le fais comme si:

button.setOnClickListener((v) -> { 
    if (_data.task != null) return; //to make sure only one thread starts at a time 
    _data.task = new MyThreadTask(); 
    _data.task.register(this); 
    _data.task.start(); //this is not thread's start, it is mine, it does other things too 
}) 

et quand le thread finit il appelle void f inished (résultat d'objets) que je manipule comme ceci:

void finished(Object result) { 
    try { 
     //get result; 
    } finally { 
     _data.task.remove(this); 
     _data.task = null; 
    } 
} 

voici mes questions:

a) est en déclarant mes méthodes d'observation synchronized nécessaire? Je l'ai fait juste pour être sûr, mais quand l'activité est détruite puis recréée, est-ce que ça se passe sur le même fil? Y a-t-il une chance par exemple qu'une progression_update puisse se produire pendant qu'un observateur est supprimé pendant onDestroy?

b) que se passe-t-il si un thread se termine et appelle post_execute (ce qui est important) lors d'un changement de configuration? la mise à jour sera-t-elle perdue?

c) Si en effet la mise à jour est perdue parce qu'elle n'a actuellement aucun observateur, existe-t-il un moyen, dans mon implémentation ou un autre, de gérer ce qui précède?

Merci d'avance pour toute aide que vous pouvez fournir

Répondre

0

La meilleure façon de garder une tâche de fond en vie grâce à un changement de configuration est en accueillant dans un retained fragment. La même instance du fragment persistera à travers le changement de configuration. Lorsque le fragment est en pause, cochez la case isChangingConfigurations de l'activité et annulez la tâche uniquement si elle est fausse.Je ne sais pas si cela est documenté n'importe où, mais il semble que l'ensemble de la modification de la configuration est publiée dans le thread principal afin que rien d'autre ne puisse fonctionner entre la pause de l'ancienne activité et la reprise du nouveau. Si vous utilisiez un AsyncTask dans un fragment conservé, vous auriez l'assurance que son onPostExecute ne pourrait pas s'exécuter pendant le changement de configuration. Avec votre approche, la tâche pourrait facilement se terminer lorsqu'il n'y a pas d'observateur. Asynctask ne gère pas les modifications de configuration correctement.

+0

Je suis d'hébergement dans un fragment conservé, MyDataFragment _data est un fragment conservé. Merci pour la méthode isChangingConfigurations, cela aidera probablement. Merci aussi pour les informations sur AsyncTask, je vais jeter un coup d'oeil au code d'AsyncTask et voir si je peux reproduire ce comportement dans mon code aussi. – Cruces

+0

Eh bien, j'ai vérifié le code d'AsyncTask, et je ne vois rien de spécial. postexecute étant exécuté lors d'un changement de configuration. Il semble utiliser un gestionnaire simple pour poster les données d'arrivée à la tâche qui à son tour appelle sur le texte, peut-être qu'il me manque quelque chose? – Cruces

+0

@Cruces Ce n'est pas dans le code de 'AsyncTask'. C'est une conséquence du fait que le framework affiche toutes les étapes du changement de configuration dans le thread principal en bloc. –

0

Je pense, au lieu de Asynctask vous devez utiliser un synctaskLoader qui peut gérer les changements de configuration facilement et il se comporte dans le cycle de vie des activités/fragments.

Lorsque vous exécutez AsyncTask et si le système Android tue votre méthode activité/fragment (lors des changements de configuration ou la conservation de la mémoire), puis votre doInBackground() maintient toujours en cours d'exécution en arrière-plan et cela peut conduire à des résultats indésirables.

Par conséquent, au lieu d'utiliser AsyncTask vous pouvez utiliser AsynctaskLoader ou si vous peuplez des données de SQLite vous pouvez utiliser CursorLoader.

Références:

  1. Guideline to choose among AsyncTaskLoader and AsyncTask to be used in Fragment

  2. https://developer.android.com/reference/android/content/AsyncTaskLoader.html

+0

Désolé je me suis trompé, quand j'ai dit mon implémentation de AsyncTask, je voulais dire que j'ai les mêmes méthodes que AsyncTask (pre_execute, do_in_background etc) mais j'utilise Thread pour exécuter des tâches en arrière-plan et Handler pour envoyer des messages au thread principal. – Cruces

+0

Aussi, ma mise en œuvre fonctionne, elle met à jour les données correctement, je me demande ce qui se passe quand les méthodes du cycle de vie s'exécutent lorsque le thread se termine – Cruces

+0

Si vous voulez gérer correctement les changements de configuration, je vous recommande AsyncTaskLoader changements pour vous. Les docs Android recommandent également la même chose. –