Dlaczego onLoadFinished jest wywoływany ponownie po wznowieniu fragmentu?
Mam dziwny problem z ładowarkami. Obecnie nie jestem pewien, czy jest to błąd w moim kodzie lub źle rozumiem ładowarki.
Aplikacja
Problem pojawia się w rozmowach (wyobraź sobie coś podobnego do Whatsapp). Używane przeze mnie ładowarki są implementowane w oparciu oPrzykład AsyncTaskLoader. Korzystam z biblioteki wsparcia.
W OnCreate uruchamiam program ładujący, aby pobrać buforowane wiadomości.Po zakończeniu CachedMessageLoader uruchamia RefreshLoader, aby pobrać (online) najnowsze wiadomości.Każdy typ programu ładującego jako odrębny identyfikator (powiedzmy offline: 1 online: 2)Działa to bardzo dobrze, z następującym wyjątkiem.
Problem
Kiedy otwieram kolejny fragment (i dodaję transakcję do backstacka), a następnie używam Back-Key, aby wrócić do konwersacji,onLoadFinished
jest wywoływany ponownie za pomocąobie wyniki z wcześniej. To wywołanie ma miejsce, zanim fragment ma szansę ponownie uruchomić program ładujący ...
To dostarczanie „starych” wyników, które otrzymałem wcześniej, skutkuje powieleniem wiadomości.
Pytanie
Dlaczego te wyniki są ponownie dostarczane?Czy używam tych ładowarek źle?Czy mogę „unieważnić” wyniki, aby upewnić się, że dostanę je tylko raz, czy też sam muszę wyeliminować duplikaty?Ślad stosu połączeń
MyFragment.onLoadFinished(Loader, Result) line: 369
MyFragment.onLoadFinished(Loader, Object) line: 1
LoaderManagerImpl$LoaderInfo.callOnLoadFinished(Loader, Object) line: 427
LoaderManagerImpl$LoaderInfo.reportStart() line: 307
LoaderManagerImpl.doReportStart() line: 768
MyFragment(Fragment).performStart() line: 1511
FragmentManagerImpl.moveToState(Fragment, int, int, int, boolean) line: 957
FragmentManagerImpl.moveToState(int, int, int, boolean) line: 1104
BackStackRecord.popFromBackStack(boolean) line: 764
...
Aktualizacja 1 Wspomniane tutaj ładowarki są inicjowane przez fragment rozmowy:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
Bundle args = getArguments();
m_profileId = args.getString(ArgumentConstants.ARG_USERID);
m_adapter = new MessageAdapter(this);
if (savedInstanceState != null) {
restoreInstanceState(savedInstanceState);
}
if (m_adapter.isEmpty()) {
Bundle bundle = new Bundle();
bundle.putString(ArgumentConstants.ARG_USERID, m_profileId);
getLoaderManager().restartLoader(R.id.loader_message_initial, bundle, this);
} else {
// Omitted: Some arguments passed in Bundle
Bundle b = new Bundle().
getLoaderManager().restartLoader(R.id.loader_message_refresh, b, this);
}
}
@Override
public void onResume() {
super.onResume();
// Omitted: setting up UI state / initiating other loaders that work fine
}
@Override
public AbstractMessageLoader onCreateLoader(final int type, final Bundle bundle) {
final SherlockFragmentActivity context = getSherlockActivity();
context.setProgressBarIndeterminateVisibility(true);
switch (type) {
case R.id.loader_message_empty:
return new EmptyOnlineLoader(context, bundle);
case R.id.loader_message_initial:
return new InitialDBMessageLoader(context, bundle);
case R.id.loader_message_moreoldDB:
return new OlderMessageDBLoader(context, bundle);
case R.id.loader_message_moreoldOnline:
return new OlderMessageOnlineLoader(context, bundle);
case R.id.loader_message_send:
sendPreActions();
return new SendMessageLoader(context, bundle);
case R.id.loader_message_refresh:
return new RefreshMessageLoader(context, bundle);
default:
throw new UnsupportedOperationException("Unknown loader");
}
}
@Override
public void onLoadFinished(Loader<Holder<MessageResult>> loader, Holder<MessageResult> holder) {
if (getSherlockActivity() != null) {
getSherlockActivity().setProgressBarIndeterminateVisibility(false);
}
// Omitted: Error handling of result (can contain exception)
List<PrivateMessage> unreadMessages = res.getUnreadMessages();
switch (type) {
case R.id.loader_message_moreoldDB: {
// Omitted error handling (no data)
if (unreadMessages.isEmpty()) {
m_hasNoMoreCached = true;
// Launch an online loader
Bundle b = new Bundle();
// Arguments omitted
getLoaderManager().restartLoader(R.id.loader_message_moreoldOnline, b, ConversationFragment.this);
}
// Omitted: Inserting results into adapter
}
case R.id.loader_message_empty: { // Online load when nothing in DB
// Omitted: error/result handling handling
break;
}
case R.id.loader_message_initial: { // Latest from DB, when opening
// Omitted: Error/result handling
// If we found nothing, request online
if (unreadMessages.isEmpty()) {
Bundle b = new Bundle();
// Omitted: arguments
getLoaderManager().restartLoader(R.id.loader_message_empty, b, this);
} else {
// Just get new stuff
Bundle b = new Bundle();
// Omitted: Arguments
getLoaderManager().restartLoader(R.id.loader_message_refresh, b, this);
}
break;
}
// Omitted: Loaders that do not start other loaders, but only add returned data to the adapter
default:
throw new IllegalArgumentException("Unknown loader type " + type);
}
// Omitted: Refreshing UI elements
}
@Override
public void onLoaderReset(Loader<Holder<MessageResult>> arg0) { }
Aktualizacja 2 Mój MainActivity (który ostatecznie hostuje wszystkie fragmenty) podklasuje SherlockFragmentActivity i zasadniczo uruchamia takie fragmenty:
Fragment f = new ConversationFragment(); // Setup omitted
f.setRetainInstance(false);
// Omitted: Code related to navigation drawer
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.fragment_container_frame, f).commit();
Fragment rozmowy rozpoczyna fragment „profilu wyświetlania” w następujący sposób:
DisplayProfileFragment f = new DisplayProfileFragment();
// Arguments omitted
FragmentManager manager = getSherlockActivity().getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.fragment_container_frame, f).addToBackStack(null).commit();