getSupportFragmentManager()导致java.lang.IllegalStateException:onSaveInstanceState后无法执行此操作

问题描述 投票:4回答:2

我试图找出原因:

getSupportFragmentManager().beginTransaction().commit();

失败了

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState

在一个非常基本的FragmentActivity类中。

所以这是我的用例(这将是一些伪代码,而不是一个完整的例子,抱歉):我有一个FragmentActivity与内部AsyncTask类。大概是这样的:

public class HelloWorld extends FragmentActivity {
    showFragment(Fragment fragment, String name) {
        getSupportFragmentManager().beginTransaction().replace(R.id.fragmentContainer, fragment, name).commit();
    }

    private class SlowFragmentShow extends AsyncTask<Context, String, Void> {
        protected Void doInBackground(Context... contexts) {
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                /* meh */
            }
        }

        protected void onPostExecute(Void nothing) {
            showFragment(new MyFragment(), "myFragment");
        }
    }
}

或者基本上,在启动应用程序10秒后,它将显示另一个片段。听起来很简单吧?这似乎也很有效,直到我决定旋转手机。当我这样做时,应用程序将在使用“无法执行此操作...”调用“getSupportFragmentManager()...”时崩溃。

android android-asynctask android-fragmentactivity illegalstateexception android-support-library
2个回答
8
投票

经过大量的调试,结果发现,当SlowFragmentShow.onPostExecute()被调用时,称为showFragment(),后者又调用了getSupportFragmentManager(),我收到了一个FragmentManager(qzxswpoi)(所以可以说我得到的例外是正确的)。我仍然不确定为什么IllegalState会在这样一个不稳定的状态下返回一个物体,但确实如此,我需要以某种方式获得“正确的”getSupportFragmentManager()。因此,为了切入追逐,我将FragmentManager存储为我的FragmentManager中的静态变量,我在调用HelloWorld FragmentActivity时更新了:

HelloWorld.onStart()

好吧,这几乎解决了它。现在我可以将手机旋转到我心中的愿望,当AsyncTask完成时,片段仍会显示。

回想起来,它看起来确实有点“哦,当然!”,但Android背后的设计决策感觉非常“异常”和不同寻常。我似乎最终只使用public class HelloWorld extends FragmentActivity { private static FragmentManager fragmentManager; public void onStart() { fragmentManager = getSupportFragmentManager(); /* more code here */ } showFragment(Fragment fragment, String name) { fragmentManager.beginTransaction().replace(R.id.fragmentContainer, fragment, name).commit(); } private class SlowFragmentShow extends AsyncTask<Context, String, Void> { protected Void doInBackground(Context... contexts) { try { Thread.sleep(10000); } catch (InterruptedException e) { /* meh */ } } protected void onPostExecute(Void nothing) { showFragment(new MyFragment(), "myFragment"); } } } 一半的代码只是为了防止它崩溃的非致命错误(例如无法更新文本字段),以及需要在try-catch(Exception)上更新的许多静态变量因为这似乎是唯一理智的方式,你可以引用Android对象,而不是在onStart()


0
投票

@VidarWahlberg你的回答很好,但是假设你有IllegalState运行任务,你打开新的FragmentActivity,在异步结束或你的应用程序在后台运行之前打开(例如:你点击了主页)。我建议你创建FragmentActivity,你扩展BaseActivity,你的所有活动应该扩展它。

什么是重要的?

FragmentActivity你需要有BaseActivity,正如@VidarWahlberg在他的回答中提到的,并在儿童活动中使用它来展示static FragmentManager fm;

有什么不同?

而不是在DialogFragment方法中初始化fm = getSupportFragmentManager();,你需要在onStart()中做到这一点。这是必要的,因为当你启动线程运行活动时,它正在创建它的新实例,当开始下一个活动时,它会创建onResume()的新实例,其中BaseActivity将被初始化,当你回到线程启动活动时,FragmenManager对象将与第二个活动(暂停)实例。应用程序将崩溃,因为您将尝试在已暂停的活动视图中显示对话框。您无法更改暂停活动的视图,因此无论何时调用fm都需要初始化。当活动开始时也会调用onResume(),因此在那里初始化onResume()是安全的!

还有什么我们需要担心的?

你可能认为一切都好,但事实并非如此。您可能没有第一次意识到它,但如果您的应用程序不在前台(但在后台),您的所有活动都会暂停,但线程已经启动并且正在运行。这是一个问题,因为线程将继续并将尝试显示FragmentManager(或者让我们说更改不在焦点上的活动视图)。所以解决方案是跟踪应用程序是否在前台或没有。这可以使用DialogFragmernt完成,但它需要清单文件中的新权限(用户不喜欢,他们可能不想安装您的应用程序)。解决方案是让PackageManager字段在static boolean isOnBackground = false;onResume()isOnBackground = false;的每个活动onPause()上更改。这不是什么大变化,因为isOnBackground = true;是我们能做到的。这是安全的,因为每次新活动的开始都会创建BaseActivity类的新实例,但由于该字段是BaseActivity,因此它只创建一次并且对于所有实例都是相同的。我们需要在开始线程后方法的主体代码之前检查static的状态。

您可能会说由于isOnBackgroundonPause()之间的延迟可能存在问题,但您的应用程序需要设计为快速启动活动的方式而不是长时间在ui线程上工作(需要在后台线程上完成)。所以,如果你的应用程序设计得很好,你就不会有问题:)

只要应用程序不在前台,您就可以使用线程运行方法的递归调用。这是安全的并且不会导致循环,因为当app的状态被重置(需要内存)或app被用户或android本身停止时循环将被停止,因为所有应用程序的实例将从内存中释放!

注意:使用android支持库!

这里是代码:

base activity.Java

onResume()

main activity.Java

.......................
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
.......................

public class BaseActivity extends FragmentActivity {

    public static FragmentManager fm;
    public static boolean isOnBackground;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    protected void onResume() {
        super.onResume();
        BaseActivity.isOnBackground = false;
        BaseActivity.fm = getSupportFragmentManager();
    }

    @Override
    protected void onPause() {
        super.onPause();
        BaseActivity.isOnBackground = true;
    }
}
最新问题
© www.soinside.com 2019 - 2024. All rights reserved.