3

我的设置如下。

我有一个从我的 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 运行时方向发生了变化,应用程序就会崩溃。

正如这里所建议的:隐藏片段我一直在尝试实现一个隐藏片段,它可以保存我的 ASyncTask 的状态。所以我所做的就是设置它,所以在我的活动的 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 .. 屏幕只是旋转.. 它工作得很好。

但是,如果我返回主菜单,然后再次单击活动,屏幕将返回空白(但不会崩溃)。我的理解是数据作为一个对象保留在片段中,但是在重新加载活动时,需要再次设置数据。需要调用 IE onLoadComplete 来填充列表/地图。

所以我得出结论,如果最初在 ASyncTask 完成后我将返回的数据保存在我的隐藏片段 onRetainInstance 中,那么我可以简单地调用 onLoadComplete 并传递它..

问题是,在这种情况下,似乎还没有调用片段,因为标签是空的,并且在 onLoadComplete 中调用回调会使应用程序崩溃。

多年来,我一直在努力解决这个问题。

我的 ASyncTask 在一个单独的类中: LoadDataFromURL 我想要实现如下 - 一个 fragmentviewpager 在屏幕上旋转 ASyncTask 在旋转/附加到新活动时保留,如果它在不应该再次运行之前完成..

任何人都可以建议。

非常感谢

编辑

将我的秘密片段中的变量更改为公共变量后,一切似乎都融合在一起了。但是因为我不是 100% 确定如何/何时调用事物,所以我不完全理解它为什么起作用。

所以.. 我调用 findOrCreateRetainFragment ,它要么创建一个新的“秘密”片段,要么返回当前实例。

如果它返回当前实例,我不会再次调用我的异步任务。如果不是,我调用我的 asynctask 并加载数据。

使用此设置,当我加载活动并旋转屏幕时,它会按预期旋转。

现在,当我返回主菜单然后再次单击活动时,它会调用异步任务。

我的理解是,在旋转时不再调用异步任务,并且 viewpager 以某种方式保存了片段。

另一方面,当我返回时,我的活动被破坏,我的秘密片段也是如此,因此当我再次单击它时,它会加载数据。这基本上是我想要的..

我是否正确理解了这一点?

谢谢

4

1 回答 1

5

您在这里遇到了一些问题(我认为)。

首先,您的回调崩溃的原因是因为它们附加到一个旧的,在屏幕方向和/或推送Activity之后不再“存在” 。Activity如果您使用将onAttach()回调附加到您的片段,则必须onDetach()在片段从Activity. 然后,每当您调用回调时,请检查是否为空,这样您就不会将数据发送到死对象。

基本上,您在这里尝试使用的逻辑是:

  1. 开始活动。

  2. 检查您的 Fragment 是否存在。如果是的话,抓住它。否则,创建它。

  3. 检索数据(如果存在)。如果没有,请等待回调。

由于回调的性质(取决于您的实现),在事件触发之前您不会收到数据。但是,如果 Activity 消失并且事件已经触发,则回调将不会执行。因此,您必须手动检索数据。使用 时setRetainInstance(),将其视为与您的 Activity 分离的实体会很有帮助。只要您不弹出当前Activity或推送新的Activity. 但是,您的 currentActivity将在屏幕方向更改时被销毁,而Fragment不会。因此,Fragment不应该依赖Activity.

对于您可能想要研究的问题,一个更优雅的解决方案是实现 Android Loader API。加载器是由系统处理的便捷工具,其工作方式大致相同,但更适合异步检索数据。它们以相同的方式有效地工作。您只需启动您的加载器和系统,如果它不存在则创建一个或重新使用一个已经存在的。LoaderManager配置更改后,它将保留在系统中。

编辑:

为了回答您的编辑,我想我会解释发生了什么。这很令人费解,所以如果有什么需要澄清的,请告诉我。

从技术上讲,片段不是您当前运行的一部分Activity。当您创建 Fragment 的实例时,您必须调用FragmentManager 上的beginTransation()and 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()以检索最后一个实例。只要它没有保留上一个 Activity 的任何上下文,就不应该有任何问题。

传统上,人们会使用它来保持对长期存在的数据的引用(就像你一样)或保持连接套接字打开,这样手机翻盖就不会删除它。

您的 ViewPager 与此无关。它如何检索片段完全取决于您如何实现它所连接的适配器。通常,保留的 Fragment 本身没有 View,因为 View 保存了它们创建的 Activity 的上下文数据。您基本上只想将其作为数据桶来保存 View 的数据,以便在它们被创建时从中提取被充气。

于 2012-12-21T14:18:42.260 回答