背景:
我正在编写一个Android应用程序,其中一个活动可以由多个片段中的任何一个填充。相对频繁地,我需要传递一束数据到活动的片段,以便它可以相应地更新UI。
为了获取当前片段,我调用 getfragmentManager()。findFragmentByTag()
并将返回的片段强制转换为我的自定义片段类。
问题:
上述方法通常运行良好。然而, findFragmentByTag()
偶尔会返回null。经过进一步的调查,我得出结论,这只会在我从Android studio运行或调试时发生(即使这样也不会每次都发生)。
相关代码:
protected void onCreate(Bundle savedInstanceState){
//Do lots of stuff
currentFragmentTag = "";
getFragmentManager().addOnBackStackChangedListener(
new FragmentManager.OnBackStackChangedListener() {
public void onBackStackChanged() {
if (getFragmentManager().getBackStackEntryCount() > 0) {
currentFragmentTag = getFragmentManager().getBackStackEntryAt(getFragmentManager().getBackStackEntryCount() - 1).getName();
}
}
});
init();
//Do some more stuff
//this should always be the case
if (currentFragmentTag.equals(LOGIN_FRAGMENT_TAG)) {
((LoginFragment) getFragmentManager().findFragmentByTag(currentFragmentTag)).beginLogin();
}
}
private void init(){
//changed to reflect George Mulligan's advice
Fragment currentFrag = getFragmentManager().findFragmentByTag(LOGIN_FRAGMENT_TAG);
if(currentFrag == null) {
currentFragmentTag = LOGIN_FRAGMENT_TAG;
getFragmentManager().beginTransaction()
.add(R.id.container, loginFrag, LOGIN_FRAGMENT_TAG)
.addToBackStack(LOGIN_FRAGMENT_TAG)
.commit();
getFragmentManager().executePendingTransactions();
}
}
public void updateFragment(){
Bundle dataBundle = new Bundle();
//put stuff in dataBundle
if (currentFragmentTag.equals(LOGIN_FRAGMENT_TAG)) {
LoginFragment currentFrag = (LoginFragment) getFragmentManager().findFragmentByTag(currentFragmentTag);
if (currentFrag != null) {
currentFrag.passBundleToFragment(dataBundle);
Log.d(TAG, "Fragment returned is valid.");
} else {
Log.d(TAG, "Fragment returned is null.");
}
}
//else if a different fragment is active then update it in the same way
}
//Manually open the loginFragment. This can be called from other fragments. My problem always occurs before this is called however.
@Override
public void openLoginScreen() {
if(/*some conditions*/) {
LoginFragment loginFrag = LoginFragment.newInstance();
getFragmentManager().beginTransaction()
.replace(R.id.container, loginFrag, LOGIN_FRAGMENT_TAG)
.addToBackStack(LOGIN_FRAGMENT_TAG)
.commit();
getFragmentManager().executePendingTransactions();
currentFragmentTag = LOGIN_FRAGMENT_TAG;
updateFragment();
}
}
通常,我的logcat看起来像这样:
Fragment returned is valid
Fragment returned is valid
Fragment returned is valid
Fragment returned is valid
Fragment returned is valid
Fragment returned is valid
Fragment returned is valid
Fragment returned is valid
...etc.
但是偶尔,只有当我从Android Studio启动应用程序时,会出现类似以下的情况:
Fragment returned is valid
Fragment returned is valid
Fragment returned is valid
Fragment returned is valid
Fragment returned is null
Fragment returned is null
Fragment returned is null
Fragment returned is null
Fragment returned is null
Fragment returned is null
Fragment returned is null
Fragment returned is null
Fragment returned is null
Fragment returned is null
Fragment returned is valid
Fragment returned is valid
Fragment returned is valid
Fragment returned is valid
Fragment returned is valid
Fragment returned is valid
Fragment returned is valid
Fragment returned is valid
Fragment returned is valid
Fragment returned is null
Fragment returned is null
Fragment returned is null
Fragment returned is null
Fragment returned is null
Fragment returned is null
Fragment returned is null
Fragment returned is valid
Fragment returned is valid
这到底是怎么回事?
更新:
我能够在断开 Android Studio 的情况下重现此错误,方法是点击应用程序切换按钮、关闭我的应用并立即重新启动它。只要我足够快地执行这些操作,就会始终按照我上面描述的方式运行。经过更多日志记录和追踪其他问题,我发现在这些特定情况下,
onCreate()
会被调用两次。我的应用程序设计为仅在横向模式下运行,部分原因是为了避免重新创建活动时出现的问题。然而,在应用程序关闭并快速重新启动时,似乎 Android 没有完成必要的旋转以使主屏幕进入纵向模式,之后再启动我的应用程序。我的假设是,这导致操作系统在应用程序运行后将其旋转回横向模式,从而重新启动应用程序。
所有这一切都很好,但它并不能解释为什么
findFragmentByTag()
有时会返回 null。我的 Activity 类中的每个对象都应该被重新创建,不是吗?那么 FragmentManager 也应该被重新初始化,对吧?还是说
getFragmentManager()
是对 Activity 本身之外的某些静态引用?第二次更新:
我尝试了 George 的想法,在调用
beginTransaction()
之前检查片段是否已被添加,虽然它没有解决问题,但我在调试时注意到了一些奇怪的地方:我在
Log.d(TAG, "Fragment returned is null.")
处设置了断点。关闭应用程序并快速重新启动它可以保证会触发这段代码,就像我上面提到的那样。然后,如果我通过在 Evaluate Expression 窗口中调用 getFragmentManager()
来查看 Fragment Manager,我会注意到一个“Login Fragment”已经被添加了,但它没有与之关联的标签。在同一应用程序会话中在
Log.d(TAG, "Fragment returned is valid.")
处设置断点,会显示 LoginFragment
已经添加,并带有预期的标签。在我的代码中从未添加过未设置标签的片段。这可能与活动被重新创建以及 Fragment manager 虽然保存了片段本身,但却丢失标签有关吗?
findFragmentById(R.id.container)
呢?这将返回当前附加到您的R.id.container
视图的片段,因此您甚至可以摆脱currentFragmentTag
变量。 - Mateusz HerychcompiledSdkVersion
是多少,只是为了确保它不依赖于它? - George MulligancompiledSdkVersion
是21。 - JohannB子片段管理器
而不是片段管理器。 - Choxmi