我试图找出原因:
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()...”时崩溃。
经过大量的调试,结果发现,当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()
。
@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
的状态。
您可能会说由于isOnBackground
和onPause()
之间的延迟可能存在问题,但您的应用程序需要设计为快速启动活动的方式而不是长时间在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;
}
}