ASyncTask,隐藏片段,保留实例和屏幕方向更改

时间:2012-12-21 13:39:15

标签: android android-asynctask android-fragments android-viewpager

我的设置如下。

我有一个从我的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以某种方式保存片段。

另一方面,当我回去时,我的活动被破坏,就像我的秘密片段一样,当我再次点击它时,它会加载数据。这基本上就是我想要的......

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

由于

1 个答案:

答案 0 :(得分:5)

这里有一些你正在经历的问题(我认为)。

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

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

  1. 开始活动。

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

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

  4. 由于回调的性质(取决于您的实现),在事件触发之前您不会收到数据。但是,如果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。通常,保留的片段本身没有视图,因为视图保存了创建它们的活动的上下文数据。您基本上只想使它成为一个数据桶,以保持视图从它们被拉出时的数据被夸大了。