3

내 응용 프로그램은 여러 조각으로 구성됩니다. 지금까지는 사용자 지정 Application 개체에 저장된 참조가 있었지만 잘못된 작업을하고 있다고 생각하기 시작했습니다.방향 변경 후 단편의 mActivity에 대한 참조가 null이됩니다. 비효율적 인 조각 상태 유지 보수

오리엔테이션 변경 후 내 조각의 mAtivity에 대한 모든 참조가 null이된다는 것을 깨달았을 때 문제가 시작되었습니다. 따라서 오리엔테이션 변경 후 getActivity()를 호출하면 NullPointerException이 발생합니다. getActivity()를 호출하기 전에 내 프래그먼트의 onAttach()가 호출되었음을 확인했지만 여전히 null을 반환합니다.

다음은 내 응용 프로그램의 유일한 활동 인 내 MainActivity를 제거한 버전입니다.

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(); 
     } 
    } 
} 

다음은 CustomApplication을 제거한 버전입니다. 이 구현의 내 생각은 응용 프로그램의 수명주기 동안 각 조각의 인스턴스를 하나만 보증하는 것이 었습니다.

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; 
    } 
} 

여러 조각을 가장 효과적으로 구현하는 방법과 상태를 유지하는 방법에 대한 도움말에 관심이 많습니다. 귀하의 정보를 위해, 내 응용 프로그램은 지금까지 15 개 이상의 조각으로 구성되어 있습니다. 몇 가지 연구를 해봤는데 FragmentManager.findFragmentByTag()가 좋은 방법 인 것 같지만 성공적으로 구현할 수 없었습니다.

내 구현은 방향 변경 이후에 mAtivity 참조가 null이된다는 사실을 제외하고는 잘 작동하는 것처럼 보였습니다. 이로 인해 일부 메모리 누수 문제가 발생할 수 있다고 생각하게되었습니다.

더 많은 코드가 필요하면 알려 주시기 바랍니다. 나는 문제가 내 Activity와 Application 구현과 관련이 있다고 강력히 믿으므로 조각 코드를 포함하는 것을 의도적으로 피했지만 잘못된 것일 수 있습니다.

감사합니다. 이 구현 뒤에

답변

10

내 생각은 어려움의 원인의 전부는 아니더라도, 내 응용 프로그램의 수명주기

이 아마 부분에 걸쳐 각 조각의 한 인스턴스를 보장하는 것이었다.

구성 변경시 안드로이드는 0이 아닌 인수 생성자를 사용하여 새 인스턴스를 생성하여 조각을 다시 만듭니다. 따라서 전역 범위 조각은 "각 조각의 인스턴스 하나만을 보장"하지 않습니다.

이 사용자 정의 Application 클래스를 삭제하십시오. 조각을 자연스럽게 다시 만들도록하거나, 단일 활동의 삶을 살아야 할 경우 setRetainInstance(true)을 사용하십시오. 여러 활동간에 조각을 재사용하지 마십시오.

+0

빠른 응답을 보내 주셔서 감사합니다. 한가지 간단한 질문 : 리소스 목적으로 Android에서 응용 프로그램이 파괴되면 사용자가 응용 프로그램을 다시 시작할 때 응용 프로그램이 파괴되기 전에 마지막으로 본 부분을 보장 할 수 있습니까? 기본 "시작 부분"대신 "시작 부분"이 표시됩니까? 또한 "메뉴"조각이 필요할 때 정확한 위치를 "선택"해야합니다. 선호도가 마음에 들지만, 더 좋은 생각이 있을까요? –

+3

@MortenSalte : "사용자가 내 애플리케이션을 다시 시작할 때 앱이 파괴되기 전에 마지막으로 보았던 부분이 표시됩니다."- onPause() 또는 onStop()에서 프로세스가 종료 된 후에도 유지 될 상태 정보 ('SharedPreferences', file, database). "선호도가 마음에 들지만, 더 좋은 생각이있을 수 있습니까?" - 환경 설정은 이와 같은 일반적인 솔루션이지만 영구 저장소가 작동해야합니다. – CommonsWare

3

mActivity에 대한 참조를 어디에서 사용하고 있는지 알 수 없습니다. 그러나 그것에 대한 언급을하지 마십시오. 오리엔테이션 변경 후 활동을 다시 만들 수 있으므로 항상 getActivity를 사용하십시오. 또한, 지금까지 세터 나 또한이 방향 변경시 모든 조각의 회원을 유지하기 위해 setRetainInstance (true)를 사용할 수있는 번들 및 인수

Best practice for instantiating a new Android Fragment

를 항상 할당 사용하여 조각의 필드를 설정하지 마십시오.

Understanding Fragment's setRetainInstance(boolean)

2

당신은 당신이 돈 '하면 현재 기준 당신에게

0

을 제공 onAttach 그렇게 재 작성되는 방향 조각을 변경할 수 있도록 할 때 조각의 onAttach 방법에 의해 제공되는 활동 개체를 사용하여이이 문제를 해결하려면 t setRetainInstance(true) in onCreate ... 컬렉션 예 : List<Object>, Vector<Object> Application 클래스에서 null을 가져옵니다. 그들을 살아있게하려면 setRetainInstance(true)을 확인하십시오. 당신이 방향을 변경하는 방법에 대한이

@Override 
public void onAttach(Context context) { 
    this.context = context; 
    super.onAttach(context); 
} 

처럼 조각에 개인 상황에 맞는 변수를 만들 수 onAttach(Context context)을 사용할 수 있습니다

0

는 onAttach는 활동에 대한 참조를 원한다면, 당신이 컨텍스트를 캐스트 할 수 있습니다, 당신에게 상황에 맞는 새로운 기준을 제공합니다 활동.