片段的引用mActivity成为方向更改后为空。无效的片段状态检修 [英] Fragment's reference to mActivity becomes null after orientation change. Ineffective fragment state maintenance

查看:231
本文介绍了片段的引用mActivity成为方向更改后为空。无效的片段状态检修的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的应用程序包含几个片段。到现在为止,我已经存储在自定义应用程序对象引用他们,但我开始觉得我做错了什么。

我的问题,开始时,我意识到,我的所有片段的引用mActivity的方向改变后变为零。所以,当我的方向改变后调用getActivity(),则抛出NullPointerException。 我检查了我的片段的onAttach()被调用之前我打这个电话来getActivity(),但它仍然返回null。

以下是我的MainActivity,这是在我的应用程序的唯一活动的一个简化版本。

 公共类MainActivity扩展BaseActivity实现OnItemClickListener,
        OnBackStackChangedListener,OnSlidingMenuActionListener {

    私人的ListView mSlidingMenuListView;
    私人SlidingMenu mSlidingMenu;

    私人布尔mMenuFragmentVisible;
    私人布尔mContentFragmentVisible;
    私人布尔mQuickAccessFragmentVisible;

    私人FragmentManager mManager的;

    @覆盖
    公共无效的onCreate(包savedInstanceState){
        super.onCreate(savedInstanceState);
        的setContentView(R.layout.activity_main);

        / *
         *布尔变量指示哪个片段3槽是在给定时间可见
         * /
        mMenuFragmentVisible = findViewById(R.id.menuFragment)!= NULL;
        mContentFragmentVisible = findViewById(R.id.contentFragment)!= NULL;
        mQuickAccessFragmentVisible = findViewById(R.id.quickAccessFragment)!= NULL;

        如果(!savedInstanceState!= NULL){
            如果(mMenuFragmentVisible&安培;!&安培; mContentFragmentVisible){
                setupSlidingMenu(真正的);
            }否则如果(mMenuFragmentVisible&安培;&安培; mContentFragmentVisible){
                setupSlidingMenu(假);
            }

            返回;
        }

        mManager的= getSupportFragmentManager();
        mManager.addOnBackStackChangedListener(本);

        最终FragmentTransaction英尺= mManager.beginTransaction();
        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);

        如果(mMenuFragmentVisible&安培;!&安培; mContentFragmentVisible){
            / *
             *只有内容片段可见,将使滑动菜单
             * /
            setupSlidingMenu(真正的);
            onToggle();

            ft.replace(R.id.contentFragment,getCustomApplication()getSportsFragment(),SportsFragment.TAG);

        }否则如果(mMenuFragmentVisible&安培;&安培; mContentFragmentVisible){
            setupSlidingMenu(假);
            / *
             *这两个菜单和内容片段可见
             * /
            ft.replace(R.id.menuFragment,getCustomApplication()getMenuFragment(),MenuFragment.TAG);
            ft.replace(R.id.contentFragment,getCustomApplication()getSportsFragment(),SportsFragment.TAG);
        }

        如果(mQuickAccessFragmentVisible){
            / *
             *快速访问片段可见
             * /
            ft.replace(R.id.quickAccessFragment,getCustomApplication()getQuickAccessFragment());
        }

        ft.commit();
    }

    私人无效setupSlidingMenu(布尔启用){
        / *
         *如果enable为真,使滑动菜单,如果假
         *禁用
         * /
    }

    @覆盖
    公共无效onItemClick(适配器视图<>母公司视图中查看,INT位置,长的id){

        //从启动菜单被点击的片段
    }

    @覆盖
    公共无效onBack pressed(){
        //会让用户preSS后退按钮时,
        //滑动菜单打开,显示的内容。
        如果(mSlidingMenu =空&安培;!&安培; mSlidingMenu.isMenuShowing()){
            onShowContent();
        } 其他 {
            super.onBack pressed();
        }
    }

    @覆盖
    公共无效onBackStackChanged(){
        / *
         *更改选定的位置时,后面的栈变化
         * /
        如果(mSlidingMenuListView!= NULL){
            mSlidingMenuListView.setItemChecked(getCustomApplication()getSelectedPosition(),TRUE);
        }
    }

    @覆盖
    公共无效onToggle(){
        如果(mSlidingMenu!= NULL){
            mSlidingMenu.toggle();
        }
    }

    @覆盖
    公共无效onShowContent(){
        如果(mSlidingMenu!= NULL){
            mSlidingMenu.showContent();
        }
    }
}
 

下面是CustomApplication的一个简化版本。我的这个实施背后的想法是,在我的应用程序的生命周期保证每一个片段的一个实例。

 公共类CustomApplication扩展应用{

    私人片段mSsportsFragment;
    私人片段mCarsFragment;
    私人片段mMusicFragment;
    私人片段mMoviesFragment;

    公共片段getSportsFragment(){
        如果(mSsportsFragment == NULL){
            mSsportsFragment =新SportsFragment();
        }

        返回mSsportsFragment;
    }

    公共片段getCarsFragment(){
        如果(mCarsFragment == NULL){
            mCarsFragment =新CarsFragment();
        }

        返回mCarsFragment;
    }

    公共片段getMusicFragment(){
        如果(mMusicFragment == NULL){
            mMusicFragment =新MusicFragment();
        }

        返回mMusicFragment;
    }

    公共片段getMoviesFragment(){
        如果(mMoviesFragment == NULL){
            mMoviesFragment =新MoviesFragment();
        }

        返回mMoviesFragment;
    }
}
 

我在如何最好地实现多个片段,以及如何保持自己的状态提示很感兴趣。为了您的信息,我applicaion由15岁以上的片段至今。 我做了一些研究,似乎FragmentManager.findFragmentByTag()是一个不错的选择,但我一直没能成功地实现它。

我的执行似乎工作良好,除了一个事实,即mActivity引用变为空方向改变后,这让我相信,我可能有一些内存泄漏问题也是如此。

如果您需要查看更多code,请让我知道。我故意回避包括片段code,因为我坚信问题与我的行为和应用的实现,但我可能是错的。

感谢您的时间。

解决方案
  

我的这个实施背后的想法是,在我的应用程序的生命周期保证每一个片段的一个实例

这可能是部分,如果不是全部,你的困难之源。

在更改配置, Android将通过使用公共的无参数的构造函数来创建一个新的实例重新创建片段。因此,您的全球范围的碎片将不会保证每一个片段的一个实例。

请删除此定制应用程序类。重新创建请允许片段自然,或者他们需要一个活动的生活居住,使用 setRetainInstance(真)。不要试图在整个活动重用片段。

My application consists of several fragments. Up until now I've had references to them stored in a custom Application object, but I am beginning to think that I'm doing something wrong.

My problems started when I realized that all my fragment's references to mActivity becomes null after an orientation change. So when I call getActivity() after an orientation change, a NullPointerException is thrown. I have checked that my fragment's onAttach() is called before I make the call to getActivity(), but it still returns null.

The following is a stripped version of my MainActivity, which is the only activity in my application.

public class MainActivity extends BaseActivity implements OnItemClickListener,
        OnBackStackChangedListener, OnSlidingMenuActionListener {

    private ListView mSlidingMenuListView;
    private SlidingMenu mSlidingMenu;

    private boolean mMenuFragmentVisible;
    private boolean mContentFragmentVisible;
    private boolean mQuickAccessFragmentVisible;

    private FragmentManager mManager;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /*
         * Boolean variables indicating which of the 3 fragment slots are visible at a given time
         */
        mMenuFragmentVisible = findViewById(R.id.menuFragment) != null;
        mContentFragmentVisible = findViewById(R.id.contentFragment) != null;
        mQuickAccessFragmentVisible = findViewById(R.id.quickAccessFragment) != null;

        if(!savedInstanceState != null) {
            if(!mMenuFragmentVisible && mContentFragmentVisible) {
                setupSlidingMenu(true);
            } else if(mMenuFragmentVisible && mContentFragmentVisible) {
                setupSlidingMenu(false);
            }

            return;
        }

        mManager = getSupportFragmentManager();
        mManager.addOnBackStackChangedListener(this);

        final FragmentTransaction ft = mManager.beginTransaction();
        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);

        if (!mMenuFragmentVisible && mContentFragmentVisible) {
            /*
             * Only the content fragment is visible, will enable sliding menu
             */
            setupSlidingMenu(true);
            onToggle();

            ft.replace(R.id.contentFragment, getCustomApplication().getSportsFragment(), SportsFragment.TAG);

        } else if (mMenuFragmentVisible && mContentFragmentVisible) {
            setupSlidingMenu(false);
            /*
             * Both menu and content fragments are visible
             */
            ft.replace(R.id.menuFragment, getCustomApplication().getMenuFragment(), MenuFragment.TAG);
            ft.replace(R.id.contentFragment, getCustomApplication().getSportsFragment(), SportsFragment.TAG);
        }

        if (mQuickAccessFragmentVisible) {
            /*
             * The quick access fragment is visible
             */
            ft.replace(R.id.quickAccessFragment, getCustomApplication().getQuickAccessFragment());
        }

        ft.commit();
    }

    private void setupSlidingMenu(boolean enable) {
        /*
         * if enable is true, enable sliding menu, if false
         * disable it
         */
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

        // launch the fragment that was clicked from the menu
    }

    @Override
    public void onBackPressed() {
        // Will let the user press the back button when
        // the sliding menu is open to display the content.
        if (mSlidingMenu != null && mSlidingMenu.isMenuShowing()) {
            onShowContent();
        } else {
            super.onBackPressed();
        }
    }

    @Override
    public void onBackStackChanged() {
        /*
         * Change selected position when the back stack changes
         */
        if(mSlidingMenuListView != null) {
            mSlidingMenuListView.setItemChecked(getCustomApplication().getSelectedPosition(), true);    
        }
    }

    @Override
    public void onToggle() {
        if (mSlidingMenu != null) {
            mSlidingMenu.toggle();
        }
    }

    @Override
    public void onShowContent() {
        if (mSlidingMenu != null) {
            mSlidingMenu.showContent();
        }
    }
}

The following is a stripped version of the CustomApplication. My thoughts behind this implementation was to guarantee only one instance of each fragment throughout my application's life cycle.

public class CustomApplication extends Application {

    private Fragment mSsportsFragment;
    private Fragment mCarsFragment;
    private Fragment mMusicFragment;
    private Fragment mMoviesFragment;

    public Fragment getSportsFragment() {
        if(mSsportsFragment == null) {
            mSsportsFragment = new SportsFragment();
        }

        return mSsportsFragment;
    }

    public Fragment getCarsFragment() {
        if(mCarsFragment == null) {
            mCarsFragment = new CarsFragment();
        }

        return mCarsFragment;
    }

    public Fragment getMusicFragment() {
        if(mMusicFragment == null) {
            mMusicFragment = new MusicFragment();
        }

        return mMusicFragment;
    }

    public Fragment getMoviesFragment() {
        if(mMoviesFragment == null) {
            mMoviesFragment = new MoviesFragment();
        }

        return mMoviesFragment;
    }
}

I am very interested in tips on how to best implement multiple fragments and how to maintain their states. For your information, my applicaion consists of 15+ fragments so far. I have done some research and it seems that FragmentManager.findFragmentByTag() is a good bet, but I haven't been able to successfully implement it.

My implementation seems to work good except for the fact that mActivity references become null after orientation changes, which lets me to believe that I may have some memory leak issues as well.

If you need to see more code, please let me know. I purposely avoided including fragment code as I strongly believe issues are related to my Activity and Application implementations, but I may be wrong.

Thanks for your time.

解决方案

My thoughts behind this implementation was to guarantee only one instance of each fragment throughout my application's life cycle

This is probably part, if not all, of the source of your difficulty.

On a configuration change, Android will re-create your fragments by using the public zero-argument constructor to create a new instance. Hence, your global-scope fragments will not "guarantee only one instance of each fragment".

Please delete this custom Application class. Please allow the fragments to be re-created naturally, or if they need to live for the life of a single activity, use setRetainInstance(true). Do not attempt to reuse fragments across activities.

这篇关于片段的引用mActivity成为方向更改后为空。无效的片段状态检修的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆