Android:LoaderCallbacks.OnLoadFinished调用了两次
我注意到使用Android加载程序和碎片时出现了奇怪的情况。当我在方向更改后调用LoaderManager.initLoader()时,不会调用onLoadFinished(尽管文档建议我应该为此做好准备),但在此之后会调用它两次。这里是谷歌集团发布的描述同样情况的帖子的链接。我编写了一个示例应用程序,其中我只在Fragment.onActivityCreated()中初始化简单加载程序,以检查是否发生了这种情况。有人注意到了吗?说 如果调用方在调用时处于started状态 请求的加载程序已存在并已生成其数据,然后 回调onLoadFinished(装入器,D) 我建议您在此时实现类似onStartLoading函数的功能 要进行快速测试,您可以尝试:Android:LoaderCallbacks.OnLoadFinished调用了两次,android,loader,Android,Loader,我注意到使用Android加载程序和碎片时出现了奇怪的情况。当我在方向更改后调用LoaderManager.initLoader()时,不会调用onLoadFinished(尽管文档建议我应该为此做好准备),但在此之后会调用它两次。这里是谷歌集团发布的描述同样情况的帖子的链接。我编写了一个示例应用程序,其中我只在Fragment.onActivityCreated()中初始化简单加载程序,以检查是否发生了这种情况。有人注意到了吗?说 如果调用方在调用时处于started状态 请求的加载程序已存在
@Override protected void onStartLoading() {
forceLoad();
}
这次启动加载background函数,然后在片段中完成onload
无论如何,如果您附加一些代码,我将尝试给您更多帮助。您可以将initLoader()方法放在片段的onResume()回调中;然后加载程序的onLoadFinished()将不再被调用两次
@Override
public void onResume()
{
super.onResume();
getLoaderManager().initLoader(0, null, this);
}
这个问题在我看来表现为一个游标加载程序返回一个已经关闭的游标:
android.database.StaleDataException: Attempted to access a cursor after it has been closed.
我猜这是一个错误或疏忽。将initLoader()移动到onResume中可能会起作用,但我所能做的是在使用完加载程序后将其删除:
要启动加载程序(在我的onCreate中):
然后在我完成之后(基本上是在onLoadFinished的末尾)
这似乎和预期的一样,没有额外的调用。问题是它调用了两次:
1.从Fragment.onStart开始
2.从FragmentActivity.onStart 唯一的区别是,在Fragment.onStart中,它检查mLoaderManager!=无效的
这意味着,如果您在启动之前调用getLoadManager,就像在onActivityCreated中一样,它将获取/创建load manager并被调用。为了避免这种情况,您需要稍后调用它,如在onResume中。从
onActivityCreated
调用initLoader
时,您可以检测旋转:
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState == null) {
// fresh new fragment, not orientation/config change
getLoaderManager().initLoader(YOUR_LOADER_ID, null, mCallbacks);
}
...
}
这样,加载程序的行为与预期一致,从而导致单个onLoadFinished
调用它不再在旋转时调用,所以如果您想要加载程序的数据,您可以将其保存在片段中,例如通过覆盖
onSaveInstanceState
编辑:我刚刚意识到,如果在装载机的
loadInBackground
期间发生旋转,则不会调用onLoadFinished
。若要解决此问题,您仍然需要在旋转后调用initLoader
,如果loader中的数据还不可用
希望对您有所帮助。您还可以比较onLoadFinished中的数据对象(加载程序,对象数据)。如果数据对象与您已有的数据对象匹配,那么在调用onLoadFinished时,您就不能执行任何操作。例如:
public void onLoadFinished(Loader loader, Object data) {
if(data != null && mData != data){
//Do something
}
}
由于对这个主题的所有探索都不可避免地在这里结束,我只想补充一下我的经验。正如@jperera所说,罪魁祸首是LoaderManager将调用onLoadFinished(),前提是加载程序已经存在。在我的例子中,我在FragmentPager中有一个片段,滚动2个标签,然后再滚动到它旁边,会导致我的旧片段开始创建自己 由于将initLoader()放在onCreate()中也会导致双重回调,因此我将initLoader()放在onResume()中。但事件序列最终是onCreate(),LoaderManager调用回调,因为加载程序存在,然后调用onResume(),触发另一个initLoader()和onLoadFinished()序列。又是一次双重回调 解决方案
我很快就找到了解决办法。加载所有数据后(如果有多个加载程序),请销毁所有加载程序,这样它们的回调就不会被额外调用 我解决了onLoadFinished这样被调用两次的问题。 在Fragment.onActivityCreated()中初始化加载程序,如下所示
if (getLoaderManager().getLoader(LOADER_ID) == null) {
getLoaderManager().initLoader(LOADER_ID, bundle, loaderCallbacks);
} else {
getLoaderManager().restartLoader(LOADER_ID, bundle, loaderCallbacks);
}
这里loaderCallbacks实现您通常的Loader回调
private LoaderManager.LoaderCallbacks<T> loaderCallbacks
= new LoaderManager.LoaderCallbacks<T>() {
@Override
public Loader<T> onCreateLoader(int id, Bundle args) {
...
...
}
@Override
public void onLoadFinished(Loader<T> loader, T data) {
...
...
}
@Override
public void onLoaderReset(Loader<T> loader) {
...
...
}
};
private LoaderManager.LoaderCallbacks LoaderCallbacks
=新建LoaderManager.LoaderCallbacks(){
@凌驾
公共加载器onCreateLoader(int-id,Bundle-args){
...
...
}
@凌驾
public void onLoadFinished(加载程序,T数据){
...
...
}
@凌驾
公共void onLoaderReset(加载器){
...
...
}
};
我不得不面对这个问题。但是我在loaderfinished方法中调用销毁加载程序(您的\u ID)
。然后加载程序不会再次调用backgrdound任务两次。如果实现AppCompatActivity,请仔细检查您在所有情况下(destroyLoader/initLoader等)是否使用getSupportLoaderManager()。我错误地将getSupportLoaderManager()与getLoaderManager()结合使用,并遇到了相同的问题。对我有效。这是虫子吗?我们应该把这个bug提交给谷歌吗?@Andrew你能解释一下为什么这样做吗<即使initLoader(…)
位于onCreate()
中,也会调用code>onLoadFinished()。好的,顺便说一句!我不知道。。。我在loader框架中发现了太多愚蠢的错误,我甚至无法开始描述。。。好的,也许不是bug,但有些实现根本不容易集成。这看起来不是一个好的修复方法。。假设您从onResume初始化加载程序中的片段开始另一个活动。。。恢复此片段将再次调用initloader。destroyLoader()
是最可靠的修复方法,尤其是在处理必须在onCreate
或onResume
生命周期之外启动的加载程序时。destroyLoader()也在销毁光标我可以销毁加载程序但保留光标吗?我有这个问题,但是,在我的例子中,我发现是从其他地方调用notifyDataSetChanged()
,导致游标被更新并调用onLoadFinished()
。
if (getLoaderManager().getLoader(LOADER_ID) == null) {
getLoaderManager().initLoader(LOADER_ID, bundle, loaderCallbacks);
} else {
getLoaderManager().restartLoader(LOADER_ID, bundle, loaderCallbacks);
}
private LoaderManager.LoaderCallbacks<T> loaderCallbacks
= new LoaderManager.LoaderCallbacks<T>() {
@Override
public Loader<T> onCreateLoader(int id, Bundle args) {
...
...
}
@Override
public void onLoadFinished(Loader<T> loader, T data) {
...
...
}
@Override
public void onLoaderReset(Loader<T> loader) {
...
...
}
};