从 Fragment 内的 AsyncTask 重新启动 Loader (SQLite) 时出现 IllegalStateException
Posted
技术标签:
【中文标题】从 Fragment 内的 AsyncTask 重新启动 Loader (SQLite) 时出现 IllegalStateException【英文标题】:IllegalStateException when restarting Loader (SQLite) from an AsyncTask inside of a Fragment 【发布时间】:2014-03-28 01:02:20 【问题描述】:我注意到我的分析报告中有一些 IllegalStateException
崩溃,我似乎无法重现。我在这里做一些结构上不正确的事情吗?我已经包含了一些伪代码,希望可以显示我的片段的骨架而不会太杂乱。在我重构代码以使用片段之前,我不记得看到过这个错误。错误发生在 AsyncTask 的onPostExecute
中。如果我能更好地澄清我的问题/伪代码,请告诉我。
public class MyListFragment extends ListFragment
implements LoaderManager.LoaderCallbacks<Cursor>
Fragment fragment = this;
SimpleCursorAdapter adapter;
// onActivityCreated initializes the adapter and the loader
public AsyncTaskLoader<Cursor> onCreateloader(int id, Bundle bundle)
return new CustomCursorLoader(this);
public void onLoadFinished(Loader<Cursor> loader, Cursor data)
adapter.swapCursor(data);
public void onLoaderReset(Loader<Cursor> loader)
adapter.swapCursor(null);
private class CustomCursorLoader extends CursorLoader
public CustomCursorLoader(Fragment fragment)
// get a handle to the OrmLite database helper class
databaseHelper = (DatabaseHelper) OpenHelperManager.getHelper(fragment.getActivity(), DatabaseHelper.class);
public Cursor loadInBackground()
// construct the Cursor that will be used for loading results from the SQLite database
private class Task extends AsyncTask
// pre execute
// grab some new database results from the server and repopulate the SQLite database using OrmLite
public void onPostExecute()
fragment.getLoaderManager().restartLoader(LOADER_ID, null, fragment);
更新
我能够通过启动Task
然后立即回击来重现该问题。当Task
完成时,包含该片段的活动未处于活动状态,因此对getLoaderManager
的调用失败。现在的问题是,处理这个问题的最优雅的方法是什么?我想我可以抓住异常。
更新 2
我相信我可以利用cancel 来阻止onPostExecute
执行。如果我这样做,我可能需要将 initLoader
移动到 onActivityCreated
以外的其他方法,以便在用户返回页面时刷新加载器(因为更新了结果,而不是加载器)。
更新 3
我想知道,就像 Robby Pond 在 cmets 中提到的那样,在这里使用 Loader 是否过大?
【问题讨论】:
请发布 logcat 输出(stacktrace)以获取更多信息 @stan0 我希望我可以。我在分析中使用EasyTracker
,所以我得到的只是异常名称和行号。我似乎无法在本地重现该问题。
你什么时候创建和执行任务?
@RobbyPond 如果数据库中当前没有结果,则在onActivityCreated
中。否则,在onOptionsItemSelected
时点击刷新操作栏项。
我不认为我们也可以用代码量。为什么要同时使用 Loader 和 AsyncTask 来查询数据库?
【参考方案1】:
我对您的案例的一些想法:
Loader 存在各种问题,尤其是与片段一起使用时。我可以建议您使用 ViewModel,但我认为它们都基于相同的代码。您可以使用普通线程,然后使用 EventBus 之类的东西来发布结果,但这可能有点过头了
我可以看到您忘记将类设置为静态,这意味着它们具有对托管类的引用。
是否可以将异步代码合并到一个 AsyncTaskLoader 中?
您可以尝试我使用 AsyncTaskLoader 的一般解决方案,here。
也许不是重新启动,而是先销毁,然后再次调用 initLoader。
AsyncTask 只应在小情况下使用,在这种情况下您不关心结果是否消失(例如,当您改变方向时)。一个很好的例子是要显示的项目列表。
我认为 Google 在 Google IO 2018 上提到,Loaders 的问题将得到修复。不确定是在支持库 28 中还是在 androidX 库中。我建议检查一下。要记住的主要事情是只在 UI 线程上使用它。
【讨论】:
以上是关于从 Fragment 内的 AsyncTask 重新启动 Loader (SQLite) 时出现 IllegalStateException的主要内容,如果未能解决你的问题,请参考以下文章
使用 recyclerview 和 Asynctask 设置 Viewpager
打开新的 Fragment 和 AsyncTask 时显示 ProgressDialog
自定义对话框片段内的进度条 - 如何从 AsyncTask 传递进度?