我的设置如下。
我有一个从我的Activity调用的FragmentPagerAdapter,它加载了两个片段。这是在onCreate中设置的。
在onResume中,我调用ASyncTask从数据库加载数据,然后通过加载数据监听器在我的活动onLoadComplete中调用回调。
@Override
public void onLoadComplete(JSONArray data) {
// TODO Auto-generated method stub
LocalFragment fragmentB = (LocalFragment)getSupportFragmentManager().findFragmentByTag(ListTag);
fragmentB.setList(data);
LMapFragment fragmentA = (LMapFragment)getSupportFragmentManager().findFragmentByTag(MapTag);
GoogleMap our_map = fragmentA.getMap();
fragmentA.plotP(myLocation,data);
}
片段由寻呼机初始化,在每个片段代码中我设置相应的标签,例如LocalFragment
@Override
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
super.onAttach(activity);
String myTag = getTag();
((PagerTest) activity).setListTag(myTag);
Log.d("what",myTag);
}
这允许我访问片段,调用其中填充列表或填充地图的函数。它有效。
我现在要做的是考虑屏幕方向的变化。如果ASyncTask正在运行时方向已更改,则应用程序崩溃。
正如这里建议的那样:Hidden Fragments我一直在尝试实现一个隐藏的片段,它保存了我的ASyncTask的状态。所以我所做的就是设置它,所以在我的Activity的onResume中我调用一个函数
static LoadDataFromURL the_data = null;
static JSONArray pub_data = null;
private static final String TAG = "RetainFragment";
public RetainFragment() {}
public static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {
RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG);
if (fragment == null) {
fragment = new RetainFragment();
fm.beginTransaction().add(fragment, TAG).commit(); // add this
}
return fragment;
}
基本上保存了我的数据。
基本上这意味着如果我旋转我的屏幕,我再也不会调用我的ASyncTask ..屏幕只是旋转..它完美地工作。
但是,如果我返回主菜单然后再次单击活动,屏幕将返回空白(但不会崩溃)。我的理解是数据作为片段中的对象保留,但重新重新加载活动时,需要再次设置数据。需要调用I.E onLoadComplete来填充列表/地图..
所以我得出结论,如果最初在ASyncTask完成后我将返回的数据保存在我的隐藏片段onRetainInstance中,那么我可以简单地调用onLoadComplete并传递它。
问题是,在这种情况下,似乎片段尚未被调用,因此标签为空,并且在onLoadComplete中调用回调会使应用程序崩溃。
我多年来一直在为此而奋斗。
我的ASyncTask是一个单独的类:LoadDataFromURL 我想要实现的目标如下 - 一个fragmentviewpager,在屏幕上旋转ASyncTask保留在旋转/附加到新活动,如果它已经完成,它不应该再次运行..
有人可以提供建议吗。
非常感谢
修改
将我的秘密片段中的变量更改为公共变量后,所有内容似乎都聚集在一起。但是因为我不能100%确定如何/何时调用事物,我不完全明白为什么它有效..
所以..我调用findOrCreateRetainFragment并创建一个新的'secret'片段或返回当前实例。
如果它正在返回当前实例,我不会再次调用我的异步任务。如果不是,我调用我的asynctask并加载数据。
使用此设置,当我加载活动并旋转屏幕时,它会按预期旋转。
现在,当我返回主菜单然后再次单击该活动时,它会调用异步任务。
我的理解是,在旋转时,不会再次调用异步任务,并且viewpager以某种方式保存片段。
另一方面,当我回去时,我的活动被破坏,就像我的秘密片段一样,当我再次点击它时,它会加载数据。这基本上就是我想要的......
我是否正确理解了这一点?
由于
答案 0 :(得分:5)
这里有一些你正在经历的问题(我认为)。
首先,回调崩溃的原因是因为它们附加到屏幕方向和/或Activity
推送后不再“存在”的旧Activity
。如果您使用onAttach()
将回调附加到您的片段,则必须使用onDetach()
在从Activity
移除片段时分离该回调。然后,每当您调用回调时,请检查是否为空,这样就不会将数据发送到死对象。
基本上,你在这里尝试使用的逻辑是:
开始活动。
检查您的片段是否存在。如果是的话,抓住它。否则,创建它。
检索数据是否存在。如果没有,请等待回调。
由于回调的性质(取决于您的实现),在事件触发之前您不会收到数据。但是,如果Activity已经消失并且事件已经触发,则不会执行回调。因此,您必须手动检索数据。使用setRetainInstance()
时,将其视为从您的Activity中分离出来的实体会很有帮助。只要您不弹出当前Activity
或推送新的Activity
,它就会存在。但是,您的当前Activity
将在屏幕方向更改时销毁,而Fragment
则不会。因此,Fragment
不应该依赖Activity
。
您可能希望查看的问题的更优雅的解决方案是实施Android Loader API。加载程序是由系统处理的方便工具,其工作方式大致相同,但与异步检索数据更加协调。他们以同样的方式有效地工作。您只需启动您的加载器和系统,如果它不存在则创建一个或重新使用已存在的系统。在配置更改时,它将由LoaderManager
保留在系统中。
修改强>
要回答您的编辑,我想我会解释发生了什么。这是令人费解的,所以只要告诉我是否需要澄清。
片段不是技术上说您当前正在运行的Activity
的一部分。创建Fragment的实例时,必须在FragmentManager上调用beginTransation()
和commit()
。 FragmentManager是一个存在于应用程序领域内的单例。 FragmentManager为您保留Fragment
的实例。然后FragmentManager将Fragment附加到您的Activity(请参阅onAttach()
)。 Fragment然后存在于FragmentManager中,这就是为什么你永远不必在你的应用程序中持有对它的引用。您只需致电findFragmentByTag/Id()
即可将其取回。
在正常情况下,当您的Activity被销毁时,FragmentManager将分离Fragment的实例(请参阅onDetach()
)并放手。 Java垃圾收集器将检测到没有对Fragment的引用,并将清理它。
当你致电setRetainInstace()
时,你告诉FragmentManager坚持下去。因此,当您的Activity在配置更改时被销毁时,FragmentManager将保留Fragment的引用。因此,重建Activity时,可以调用findFragmentByTag/Id()
来检索最后一个实例。只要它没有保留上一个活动的任何上下文,就不应该有任何问题。
传统上,人们会用它来保持对长期数据的引用(就像你一样)或保持连接套接字打开,这样手机翻转就不会删除它。
您的ViewPager与此无关。它如何检索Fragments完全取决于您如何实现它附加到的Adapter。通常,保留的片段本身没有视图,因为视图保存了创建它们的活动的上下文数据。您基本上只想使它成为一个数据桶,以保持视图从它们被拉出时的数据被夸大了。