片段恢复后,为什么再次调用onLoadFinished? [英] Why is onLoadFinished called again after fragment resumed?

查看:123
本文介绍了片段恢复后,为什么再次调用onLoadFinished?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对装载机有一个特殊的问题。目前,我不确定这是否是我的代码中的错误或我误解了加载程序。

I have a peculiar issue with Loaders. Currently I am unsure if this is a bug in my code or I misunderstand loaders.

该应用程序

问题出在对话中(想像类似于Whatsapp)。
我使用的加载程序是根据 AsyncTaskLoader示例实现的。我正在使用支持库。

The issue arises with conversations (imagine something similar to Whatsapp). The loaders I use are implemented based on the AsyncTaskLoader example. I am using the support library.


  • 在OnCreate中,我启动加载程序以检索缓存的消息。

  • 当CachedMessageLoader完成时,它会启动RefreshLoader来检索(在线)最新消息。

  • 每个加载程序类型均作为唯一的ID(例如,offline:1 online:2)

这很好,但有以下例外。

This works very well, with the following exception.

问题

当我打开另一个片段(并将事务添加到Backstack),然后使用Back-Key返回对话片段时,<$ c再次调用$ c> onLoadFinished 并返回之前的两个结果。
此调用发生在片段有机会再次启动加载程序之前...

When I open another fragment (and add the transaction to the backstack) and then use the Back-Key to go back to the conversationFragment, onLoadFinished is called again with both results from before. This call happens before the fragment has had any chance to start a loader again...

这是我在获取结果之前获得的旧结果的传递重复的消息。

This delivering of "old" results that I obtained before results in duplicated messages.

问题


  • 为什么要交付这些结果再次?

  • 我是否错误地使用了这些装载机?

  • 我可以使结果无效以确保只交付一次吗?必须自己消除重复项?

堆栈调用跟踪

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
此处提到的加载程序由会话片段启动:

Update 1 The loaders mentioned here are initiated by the conversation fragment:

@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) { }

更新2
My MainActivity(最终托管所有片段)对SherlockFragmentActivity进行子类化,并基本上启动如下片段:

Update 2 My MainActivity (which ultimatively hosts all fragments) subclasses SherlockFragmentActivity and basically launches fragments like this:

    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();

对话片段开始显示配置文件片段,如下所示:

The conversation fragment starts the "display profile" fragment like this:

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();


推荐答案

还有其他类似的问题,例如Android:LoaderCallbacks.OnLoadFinished被调用了两次但是,加载器管理器挂钩的行为是它们的本质。您可以在获得第一组结果后销毁加载程序

There are other similar questions such as Android: LoaderCallbacks.OnLoadFinished called twice However the behavior of the loader manager hooks are what they are. You can either destroy the loader after getting the first set of results

public abstract void destroyLoader (int id)

或者您可以处理onLoaderReset并将您的UI数据与加载器数据更紧密地联系

or you can handle the onLoaderReset and tie your UI data more closely to the loader data

public abstract void onLoaderReset (Loader<D> loader)




在重置先前创建的加载程序时调用,因此
使其数据不可用。应用程序此时应该
删除它对加载程序数据的所有引用。

Called when a previously created loader is being reset, and thus making its data unavailable. The application should at this point remove any references it has to the Loader's data.

我个人而言,我将使用ContentProvider并为此使用一个CursorLoader(每行数据都必须具有唯一的_ID,但对于消息来说应该没有问题)。

Personally, I would use a ContentProvider and a CursorLoader for this (each row of data would need to have a unique _ID but for messages that should not be a problem).

这篇关于片段恢复后,为什么再次调用onLoadFinished?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆