Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/336.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Android findFragmentByTag在调试时返回null_Java_Android_Android Fragments_Android Studio - Fatal编程技术网

Java Android findFragmentByTag在调试时返回null

Java Android findFragmentByTag在调试时返回null,java,android,android-fragments,android-studio,Java,Android,Android Fragments,Android Studio,背景: protected void onCreate(Bundle savedInstanceState){ //Do lots of stuff currentFragmentTag = ""; getFragmentManager().addOnBackStackChangedListener( new FragmentManager.OnBackStackChangedListener() { public

背景:

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();
    }
}
我正在编写一个android应用程序,其中的一个活动可以由许多片段中的一个填充。通常,我需要将一组数据传递给活动片段,以便它能够相应地更新UI

要获取当前片段,我调用
getfragmentManager().findFragmentByTag()
,并将返回的片段强制转换为我的自定义片段类

问题:

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();
    }
}
上述方法通常效果良好。但是,
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

我的活动类中的每个对象都应该重新创建,对吗?那么FragmentManager不也应该重新初始化吗?或者
getFragmentManager()
是对活动本身之外的内容的静态引用

第二次更新:

在调用
beginTransaction()
之前,我尝试了George关于检查片段是否已经添加的想法,尽管这没有解决问题,但在调试时我注意到了一些奇怪的事情:

我在
Log.d(标记“Fragment returned is null”)上设置了一个断点。关闭应用程序并快速重新启动它可以保证达到我上面提到的代码。然后,如果我通过调用Evaluate Expression窗口中的
getFragmentManager()
来查看片段管理器,我注意到已经添加了一个“Login Fragment”,但它没有与之关联的标记

Log.d处设置断点(标记“返回的片段有效”)显示了
LoginFragment
添加了预期的标记


在我的代码中,我从来没有在不设置标记的情况下添加片段。这可能与正在重新创建的活动以及片段管理器丢失标记有关,即使它保留了片段本身?

好的,这应该是一条注释,但是,代码格式与注释一样难看。也许你的片段被暂停了。 试试这个

@Override
public void onResume() {
    super.onResume();
    updateFragment();
}

这不是对你问题的直接回答,而是一个从一开始就避免问题的建议

因为片段和活动生命周期之间的相互作用是如此复杂和难以推理,我发现,如果将两者完全解耦,就会容易得多


我设置了一个消息总线(例如Otto或GreenRobot),在您的情况下,将让活动将消息发布到总线,并让片段订阅该消息,而不是获取对片段的引用并直接调用其上的方法。更干净,如果片段恰好没有准备好(还没有在总线上注册),则不会出现错误情况。

需要注意的几点。 在
updateFragment
中,您应该真正使用
.equals
来比较字符串,而不是引用比较
=
so
LOGIN\u FRAGMENT\u TAG.equals(currentFragmentTag)
作为最佳实践并避免混淆

另外,
FragmentManager
将记住您在方向更改时添加到其中的片段。因此,在您提到的一个示例中,您两次进入
onCreate
,您的
LoginFrag
的两个实例很可能位于后堆栈上

您可以通过在
updateFragment
方法中执行相同的查找来避免这种情况,并且仅在未找到片段时添加片段

LoginFragment currentFrag = (LoginFragment) getFragmentManager()
    .findFragmentByTag(LOGIN_FRAGMENT_TAG);

if(currentFrag == null) {
    currentFrag = getFragmentManager().beginTransaction()
        .replace(R.id.container, loginFrag, LOGIN_FRAGMENT_TAG)
        .addToBackStack(LOGIN_FRAGMENT_TAG)
        .commit();

    //executePendingTransactions() usually isn't necessary...
    getFragmentManager().executePendingTransactions();
}
我猜在这个活动中使用了多个片段,因为您需要使用字符串
currentFragmentTag
,但是如果没有,那么您最好在类上保留对
LoginFragment
的引用作为字段,这样您就可以避免在
updateFragment
方法中额外查找它


除此之外,我尝试复制您的错误,但我无法复制,因此错误可能在您未显示的代码中的其他地方。

谢谢您的回复。问题是我需要经常调用'updateFragment()'(例如,每当gps位置发生变化时)。所以我真的无法准确预测它什么时候会被调用。让我感兴趣的是,只有当我从Android StudioInterest运行时,这种情况才会发生。然而,重新编写代码以使用这样的总线需要相当多的工作(和时间)。但是如果没有别的办法,我会考虑在字符串上进行良好的调用。你认为我使用了多个片段是对的。我按照您的建议在
beginTransaction
之前添加了空检查。它没有解决我的问题,但我