【发布时间】:2016-04-03 14:49:53
【问题描述】:
背景:
我正在编写一个 android 应用程序,其 Activity 可以由多个片段之一填充。相对而言,我需要将一组数据传递给活动片段,以便它可以相应地更新 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() 有时会返回空值。
我的 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 添加了预期的标签。
在我的代码中,我没有设置标签就添加片段。这是否与正在重新创建的活动和片段管理器丢失标签有关,即使它保留了片段本身?
【问题讨论】:
-
根据您的更新,您是否在活动中覆盖
onSaveInstanceState?如果是这样,请确保您拨打的是super.onSaveInstanceState。我相信这是保存片段状态的地方。 -
我没有覆盖
onSaveInsanceState -
很奇怪。您可以发布重现问题的活动和片段的简化版本吗?它一定是某处简单的东西......我想不出除了对片段管理器或片段本身做错什么之外,可能导致片段丢失其标签的原因。
-
确实很奇怪。这不是您问题的答案,但是使用
findFragmentById(R.id.container)代替呢?这将为您返回当前附加到您的R.id.container视图的 Fragment,因此您甚至可以摆脱currentFragmentTag变量。 -
另外,您的
compiledSdkVersion是什么,以确保它不依赖于它?
标签: java android android-fragments android-studio