无法在onLoadFinished中执行此操作

我正在创build一个新的示例Android应用程序与一个活动和许多片段。

首先我要开始“OverviewFragment”(我的仪表板),它应该显示一些基本的信息。 我的OverviewFragment用“AccountProvider”(ContentProvider)加载信息。 如果数据库是空的,则应该用我的WelcomeFragmentreplaceOverviewFragment …

这里是一些代码:

public class OverviewFragment extends BaseFragment implements LoaderManager.LoaderCallbacks { private static final int LOADER_ACCOUNTS = 10; private static final int LOADER_TRANSACTIONS = 20; @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); getLoaderManager().initLoader(LOADER_ACCOUNTS, null, this); //getLoaderManager().initLoader(LOADER_TRANSACTIONS, null, this); } @Override public Loader onCreateLoader(int id, Bundle args) { Uri uri = null; String[] projection = null; String selection = null; String[] selectionArgs = null; String sortOrder = null; switch (id) { case LOADER_ACCOUNTS: uri = WalletProvider.CONTENT_URI; projection = new String[]{DBHelper.ACC_ID, DBHelper.ACC_TITLE}; sortOrder = DBHelper.ACC_TITLE; break; } CursorLoader cl = new CursorLoader(getActivity(), uri, projection, selection, selectionArgs, sortOrder); return cl; } @Override public void onLoadFinished(Loader loader, Object data) { switch (loader.getId()) { case LOADER_ACCOUNTS: bindAccounts((Cursor) data); break; } } @Override public void onLoaderReset(Loader loader) { } private void bindAccounts(Cursor cursor) { boolean showCreateWallet = true; if (cursor != null && cursor.moveToFirst()) { showCreateWallet = false; } if (showCreateWallet) { listener.changeFragment(new WalletCreateFragment()); } } 

在这里我的主要活动

  @Override public void changeFragment(Fragment fragmentToLoad) { FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.beginTransaction() .replace(R.id.container, fragmentToLoad) .commit(); } 

现在…如果我用一个空的数据库开始我的应用程序,我得到了你在标题中看到的错误。

我知道我不应该改变onLoadFinished函数中的片段….但我可以在哪里做到这一点? :P

对不起,我的英文=)

Solutions Collecting From Web of "无法在onLoadFinished中执行此操作"

这是一个android的错误。 为了解决这个问题,使用Handler来replace/添加/调用onLoadFinished内部的片段,如下所示:

 final int WHAT = 1; Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { if(msg.what == WHAT) changeFragment(fragmentToLoad); } }; handler.sendEmptyMessage(WHAT); 

这不是一个错误,但这不是一个简单的问题。 onLoadFinished可以随时运行,包括在活动状态被保存之后。 结果片段的变化将不会自动被框架保存。 你必须明确地允许这个使用commitAllowingStateLoss

onLoadFinished的文档中:

之前创build的加载器完成加载时调用。 请注意,通常应用程序不允许在此调用中提交碎片事务,因为它可能在保存活动状态后发生。 有关这方面的进一步讨论,请参阅FragmentManager.openTransaction()。

从另一个答案的评论中引用的线程引用Dianne Hackborn:

只要明白,如果活动的片段状态已经被保存,你的代码将需要正确地更新它,如果它以后从状态重新启动。

[…]

作为加载程序的结果,显示一个对话框(或在UI中进行任何其他主要的转换)是不好的用户体验。 下面是你正在做的事:在背景中启动一些操作,以在一定的时间内完成,在完成时可以在用户面前抛出一些东西,把它们从他们正在做的事中撤出。

我的build议是以同样的方式向用户显示有关加载程序结果的任何信息。

如果您(和您的用户)对用户体验commitAllowingStateLoss ,则可以使用commitAllowingStateLoss更新片段。

两个答案都缺less一个好的解决scheme。 确实,在onLoadfinished已完成的事情上做一些事情并不是一个好主意,因为活动或碎片可能已经被破坏了。

在单独线程上的某些内容发生更改后更新UI也不是一个好的用户体验,因为他们可能正在执行某个操作。 但是,这可以通过刷新button修复,然后更新用户所看到的内容。

但是,在某些情况下,您可能需要实际更新UI。 例如,如果我们需要执行一个操作,需要用户在按下下一个button之后等待。

在你的例子中,你直接通过Fragment扩展Callbacks ,所以你可以在你的片段上创build一个方法,使你可以像这样安全地执行UI更新:

 private void update(Runnable runnable) { View rootView = getView(); if (isAdded() && rootView != null) { rootView.post(runnable); } } 

这将检查片段是否处于可以处理UI更新并运行命令的状态。 它可以像这样使用:

 update(new Runnable() { @Override public void run() { bindAccounts((Cursor) data); } }) 

也就是说,使用你的片段作为callback并不是一个明智的想法,因为它可能泄漏你的片段在Activity上的LoaderManager中。

另一个安全的方法来处理这些将与广播接收机(确保他们未注册)。

但是,最安全的select是在Android准备投入使用时使用新发布的架构组件:

https://developer.android.com/topic/libraries/architecture/index.html