我对装载机有一个奇怪的问题.目前我不确定这是否是我的代码中的错误或我误解了加载器.
该应用程序
问题出现在对话中(想象类似于Whatsapp的东西).我使用的加载器是基于AsyncTaskLoader示例实现的.我正在使用支持库.
在OnCreate中,我启动一个加载器来检索缓存的消息.
当CachedMessageLoader完成时,它启动RefreshLoader以检索(在线)最新消息.
每个加载器类型作为不同的ID(例如,离线:1在线:2)
这非常有效,但有以下例外.
问题
当我打开另一个片段(并将事务添加到backstack)然后使用Back-Key返回conversationFragment时,onLoadFinished
再次使用之前的两个结果调用.此调用发生在片段有机会再次启动加载器之前...
我之前获得的"旧"结果的传递导致重复的消息.
题
为什么这些结果再次传递?
我是否错误地使用这些装载机?
我可以"使结果无效"以确保我只将它们送到一次或者我必须自己消除重复吗?
堆栈跟踪的电话
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 ...
更新1 此处提到的加载器由对话片段启动:
@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> loader, Holder holder) { if (getSherlockActivity() != null) { getSherlockActivity().setProgressBarIndeterminateVisibility(false); } // Omitted: Error handling of result (can contain exception) List 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 > arg0) { }
更新2 我的MainActivity(最终托管所有片段)子类SherlockFragmentActivity并基本上启动这样的片段:
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();
会话片段启动"显示配置文件"片段,如下所示:
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();
Eric Woodruf.. 7
还有其他类似的问题,比如Android:LoaderCallbacks.OnLoadFinished两次调用但是加载器管理器挂钩的行为就是它们的本质.您可以在获取第一组结果后销毁加载程序
public abstract void destroyLoader (int id)
或者您可以处理onLoaderReset并将UI数据与加载器数据更紧密地联系起来
public abstract void onLoaderReset (Loaderloader)
在重置先前创建的加载程序时调用,从而使其数据不可用.此时,应用程序应删除它对Loader数据的任何引用.
就个人而言,我会使用ContentProvider和CursorLoader(每行数据都需要一个唯一的_ID,但对于不应该有问题的消息).
还有其他类似的问题,比如Android:LoaderCallbacks.OnLoadFinished两次调用但是加载器管理器挂钩的行为就是它们的本质.您可以在获取第一组结果后销毁加载程序
public abstract void destroyLoader (int id)
或者您可以处理onLoaderReset并将UI数据与加载器数据更紧密地联系起来
public abstract void onLoaderReset (Loader<D> loader)
在重置先前创建的加载程序时调用,从而使其数据不可用.此时,应用程序应删除它对Loader数据的任何引用.
就个人而言,我会使用ContentProvider和CursorLoader(每行数据都需要一个唯一的_ID,但对于不应该有问题的消息).