为什么在片段恢复后再次调用onLoadFinished?

 叶蕊2502860197 发布于 2023-02-04 18:27

我对装载机有一个奇怪的问题.目前我不确定这是否是我的代码中的错误或我误解了加载器.

该应用程序

问题出现在对话中(想象类似于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 (Loader loader)

在重置先前创建的加载程序时调用,从而使其数据不可用.此时,应用程序应删除它对Loader数据的任何引用.

就个人而言,我会使用ContentProvider和CursorLoader(每行数据都需要一个唯一的_ID,但对于不应该有问题的消息).

1 个回答
  • 还有其他类似的问题,比如Android:LoaderCallbacks.OnLoadFinished两次调用但是加载器管理器挂钩的行为就是它们的本质.您可以在获取第一组结果后销毁加载程序

    public abstract void destroyLoader (int id)
    

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

    public abstract void onLoaderReset (Loader<D> loader)
    

    在重置先前创建的加载程序时调用,从而使其数据不可用.此时,应用程序应删除它对Loader数据的任何引用.

    就个人而言,我会使用ContentProvider和CursorLoader(每行数据都需要一个唯一的_ID,但对于不应该有问题的消息).

    2023-02-04 18:29 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有