J'ai remarqué une situation étrange en utilisant Android Loaders and Fragments. Lorsque j'appelle LoaderManager.initLoader () après que le changement d'orientation onLoadFinished n'est pas appelé (bien que la documentation suggère que je devrais y être préparé)), mais il est appelé deux fois après cela. Voici un lien pour publier dans des groupes Google qui décrivent la même situation https://groups.google.com/forum/?fromgroups#!topic/Android-developers/aA2vHYxSsk . J'ai écrit un exemple application dans laquelle je n'initie que Loader simple dans Fragment.onActivityCreated () pour vérifier si cela se produit et si c'est le cas.
Vous pouvez placer la méthode initLoader () dans le rappel onResume () de votre fragment; alors onLoadFinished () du chargeur ne sera plus appelé deux fois.
@Override
public void onResume()
{
super.onResume();
getLoaderManager().initLoader(0, null, this);
}
Ce problème s'est manifesté pour moi avec un CursorLoader retournant un curseur déjà fermé:
Android.database.StaleDataException: Attempted to access a cursor after it has been closed.
Je suppose que c'est un bug ou une erreur. Alors que déplacer initLoader () dans onResume peut fonctionner, ce que j'ai pu faire a été de supprimer le chargeur lorsque j'en ai fini:
Pour démarrer le chargeur (dans mon onCreate):
getLoaderManager().initLoader(MUSIC_LOADER_ID, null, this);
Ensuite, après que j'en ai fini (essentiellement à la fin de onLoadFinished)
getLoaderManager().destroyLoader(MUSIC_LOADER_ID);
Cela semble se comporter comme prévu, pas d'appels supplémentaires.
Si au moment de l'appel, l'appelant est dans son état démarré et que le chargeur demandé existe déjà et a généré ses données, alors rappel onLoadFinished (Loader, D)
Je vous suggère d'implémenter quelque chose comme la fonction onStartLoading à ce exemple
Pour un test rapide, vous pouvez essayer:
@Override protected void onStartLoading() {
forceLoad();
}
Cela lance la fonction loadInBackground puis onLoadFinished in Fragment.
De toute façon, si vous joignez du code, je vais essayer de vous aider davantage.
J'ai résolu le problème d'appeler onLoadFinished deux fois comme ça. Dans votre Fragment.onActivityCreated () init votre chargeur comme ceci
if (getLoaderManager().getLoader(LOADER_ID) == null) {
getLoaderManager().initLoader(LOADER_ID, bundle, loaderCallbacks);
} else {
getLoaderManager().restartLoader(LOADER_ID, bundle, loaderCallbacks);
}
ici loaderCallbacks implémente vos rappels Loader habituels
private LoaderManager.LoaderCallbacks<T> loaderCallbacks
= new LoaderManager.LoaderCallbacks<T>() {
@Override
public Loader<T> onCreateLoader(int id, Bundle args) {
...
...
}
@Override
public void onLoadFinished(Loader<T> loader, T data) {
...
...
}
@Override
public void onLoaderReset(Loader<T> loader) {
...
...
}
};
Le problème est qu'il a appelé deux fois:
1. de Fragment.onStart
2. de FragmentActivity.onStart
La seule différence est que dans Fragment.onStart, il vérifie si mLoaderManager! = Null. Cela signifie que si vous appelez getLoadManager avant onStart, comme dans onActivityCreated, il obtiendra/créera un gestionnaire de charge et il sera appelé. Pour éviter cela, vous devez l'appeler plus tard, comme dans onResume.
Lorsque vous appelez initLoader
depuis onActivityCreated
, vous pouvez détecter la rotation:
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState == null) {
// fresh new fragment, not orientation/config change
getLoaderManager().initLoader(YOUR_LOADER_ID, null, mCallbacks);
}
...
}
De cette façon, le chargeur se comporte comme prévu, résultant en un seul appel onLoadFinished
.
Il n'est plus appelé lors de la rotation, donc si vous voulez les données du chargeur, vous pouvez les garder dans votre fragment, par exemple en remplaçant onSaveInstanceState
.
Éditer:
Je viens de réaliser que onLoadFinished
ne sera pas appelé si la rotation se produit pendant le loadInBackground
du chargeur. Pour résoudre ce problème, vous devez toujours appeler initLoader
après la rotation si les données du chargeur ne sont pas encore disponibles.
J'espère que cela pourra aider.
Puisque toutes les recherches sur ce sujet finissent inévitablement ici, je voulais juste ajouter mon expérience. Comme l'a dit @jperera, le coupable était que LoaderManager appellera onLoadFinished () si les chargeurs existent déjà. Dans mon cas, j'avais des fragments dans un FragmentPager et en faisant défiler 2 onglets, puis en faisant défiler à côté de lui, mon ancien fragment commencerait à se créer.
Comme placer initLoader () à l'intérieur de onCreate () provoque également des rappels doubles, j'ai placé initLoader () à l'intérieur de onResume (). Mais la séquence d'événements finit par être onCreate (), LoaderManager appelle des rappels puisque les chargeurs existent, puis onResume () est appelé, déclenchant une autre séquence initLoader () et onLoadFinished (). IE, un autre double rappel.
J'ai trouvé une solution rapide par "Matt" . Une fois toutes vos données chargées (si vous avez plusieurs chargeurs), détruisez tous les chargeurs pour que leurs rappels ne soient pas appelés plus longtemps.
Si vous implémentez AppCompatActivity, vérifiez que vous utilisez getSupportLoaderManager () dans les cas tous (destroyLoader/initLoader, etc.). J'avais utilisé par erreur getSupportLoaderManager () en conjonction avec un getLoaderManager () et j'ai rencontré le même problème.
j'ai fait face à ce problème. mais j'ai utilisé pour appeler la destroyloader(YOUR_ID)
dans les méthodes loaderfinished. alors le chargeur n'appelle pas à nouveau la tâche d'arrière-plan deux fois.
Vous pouvez également comparer l'objet de données dans onLoadFinished (chargeur de chargeur, données d'objet). Si l'objet de données correspond à celui que vous avez déjà, vous ne pouvez rien faire lorsque onLoadFinished est appelé. Par exemple:
public void onLoadFinished(Loader loader, Object data) {
if(data != null && mData != data){
//Do something
}
}