Fragment에 대해서
Fragment를 사용해본적이 있으나 왜 Fragment를 사용하는지 잘 모르는 분들께 유용할것 같습니다.
안드로이드에서 프레그먼트는 액티비티와 마찬가지로 별도의 레이아웃을 가지며 독자적인 LifeCycle을 가진다. 프레그먼트는 액티비티보다 좀 더 유연하여 액티비티 내에 포함될 수 있다.
1. Fragment가 나오기 까지..
기기의 성능이 좋아지고, 화면이 커지면서 하나의 화면에 들어가는 앱 기능이 많아졌다.
그 결과 한 Activity나 View에 많은 코드가 들어가는 경우가 늘어났고, 코드상 이를 나누고자 하는 노력이 필요했다.
View와 ViewGroup > include > ActivityGroup > Fragement
-
View와 ViewGroup
레이아웃을 만들때는 Java코드로 개발하지 않고 뷰와 뷰그룹만으로도 만들 수 있다.
때론 특별한 기능의 View는 개발자가 CustomView로 코드 분리하여 만들 수 있다. -
include
include 속성을 이용하면 xml 코드도 분리할 수 있다.
하지만 include는 공통적인 레이아웃을 모아서 중복을 줄일 수 있다는 장점 이외에는 없다. -
ActivityGroup
ActivityGroup을 이용하면 한 화면에 여러 액티비티를 보여줄 수 있어 기존보다 훨씬 강력한 방법으로 화면 구성이 가능하다. -
Fragment
Fragment는 기존 ActivityGroup의 단점을 개선한 것이다. 보다 완벽하게 화면을 분리하고 유연하게 대처할 수 있다.
1.1. ActivityGroup
ActivityGroup은 하나의 Activity에 여러개의 Activity를 포함할 수 있게 해준다. ViewGroup이 View를 가질수 있듯 ActivityGroup은 Activity를 가질 수 있다.
참고로 ActivityGroup 역시 Activity이다. 여러 Activity를 포함할 상위 Activity는 ActivityGroup을 상속받는다.
ActivityGroup을 사용하면 하나의 화면에 전환해야할 뷰가 여러개 존재할 때 각각의 뷰를 별도의 액티비티 단위로 나눌 수 있기 때문에 관리하기가 좋다.
하지만 ActivityGroup은 자체가 Activity로의 기능을 제대로 수행하지 못하는 문제도 있고, 내부에 포함되는 Activity의 개념이 모호해 진다는 단점이 존재한다.
ActivityGroup은 Deprecated 되었다. 관련 내용은 ActivityGroup에 대해서 포스팅을 참고할 것.
2. Fragment 기초
최초 ActiityGroup이 필요했던 이유는 액티비티가 너무 커지면서 이를 분리하여 관리하고자 필요가 있었기 때문이다. Fragment는 이를 완벽하게 대체한다.
Fragment를 쉽게 설명하면 별도의 뷰그룹인데 액티비티처럼 일정부분 필요한 생명주기가 존재한다. 생명주기가 자체적으로 존재하기 때문에 액티비티가 모든 생명주기를 관리할 필요가 없다는 장점이 있다.
Fragment를 Activity에 포함한다는 것은 간단하게 Activity의 뷰컨테이너에 Fragment의 뷰를 포함한다는 의미다.
FragmentActivity는 기존 Activity의 생명주기뿐 아니라 포함된 Fragment의 생명주기도 같이 호출해준다는 점이 차이가 있다.
FragmentActivity내에는 FragmentManager가 존재하여 Fragment의 추가,삭제,변경 등을 담당한다.
참고로 Fragment를 중첩하여 사용할 수도 있다. 이 때는 Fragment가 Fragment를 관리해야 하는데 이 때문에 FragmentManager 뿐 아니라 ChildFragmentManager 가 존재한다.
2.1. Fragment 의 구조
public class TextViewerFragment extends Fragment {
// 텍스트 뷰어 프래그먼트 객체를 생성하는 함수다.
public static TextViewerFragment newInstance() {
TextViewerFragment f = new TextViewerFragment();
return f;
}
// 부모 액티비티는 해당 프래그먼트를 구동하고, 액티비티에 추가될 프래그먼트의 레이아웃을 onCreateView 함수의 반환값으로 요구한다.
// 따라서 해당 프래그먼트는 onCreateView 재정의 함수에서 자신의 레이아웃을 생성하고 반환한다.
@Override
public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState ) {
return inflater.inflate( R.layout.fragment_text_viewer, container, false);
}
}
코드의 onCreateView()를 보면 알겠지만 결국 액티비티의 뷰그룹에 Fragment의 뷰를 포함하는 형태이기 때문에 Fragment에서 View를 리턴해 준다.
아래는 Activity의 XML 이다.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<!-- ① 좌측 영역에 메뉴 리스트 프래그먼트를 설정한다. -->
<fragment class="com.superdroid.fragment.ListMenuFragment"
android:id="@+id/menu_fragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
<!-- ② 우측 영역에 텍스트뷰어 혹은 이미지뷰어 프래그먼트를 포함할 프래그먼트 컨테이너다. -->
<FrameLayout android:id="@+id/viewer_fragment_container"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
</LinearLayout>
좌측에는 고정된 Fragment를 배치하기에 그대로 Fragment를 배치하였고, 우측은 여러 Fragment를 교체할 것이기 때문에 FrameLaout을 둔것이다.
여기서 중요한점은 레이아웃 구조에 View나 ViewGroup이 아닌 fragment가 포함이 될 수 있는가를 봐야한다.
xml을 보면 마치 fragment가 view나 viewGroup처럼 레이아웃 구조에 포함이 된것처럼 보이는데 fragment는 view나 viewGroup이 아니다.
단지 안드로이드에서 xml을 해석하는 중 fragment 를 만나면 해당 fragment를 생성하고 fragment의 레이아웃을 view에 그리게 된다.
일종의 include 와 동일하다.
2.2. FragementActivity의 구조
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// ② 액티비티 레이아웃 우측에 텍스트뷰어 프래그먼트를 추가한다.
// ====================================================================
TextViewerFragment textViewerFragment = TextViewerFragment.newInstance();
getFragmentManager().beginTransaction().add( R.id.viewer_fragment_container, textViewerFragment ).commit();
// ③ 액티비티 레이아웃 좌측에 리스트 메뉴 프래그먼트에서 아이템을 선택했을 때
// 이벤트를 처리하기 위한 리스너를 구현 및 등록한다.
ListMenuFragment listMenuFragment = (ListMenuFragment)getFragmentManager().findFragmentById(R.id.menu_fragment );
listMenuFragment.setOnListItemClickListener(new ListMenuFragment.OnListItemClickListener() {
@Override
public void onItemClick( int itemType ) {
// ④ 액티비티 우측 영역 프래그먼트 컨테이너에 현재 보여지고 있는 프래그먼트를 참조한다.
// 만일 선택된 아이템이 현재 보여지고 있는 프래그먼트라면 아무 처리도 하지 않고 끝내고,
// 아니라면 보여줘야 할 프래그먼트를 생성해둔다.
Fragment fragment = getFragmentManager().findFragmentById( R.id.viewer_fragment_container );
if( itemType == ListMenuFragment.ITEM_TYPE_TEXT_VIEWER) {
if( fragment instanceof TextViewerFragment == true ) {
return;
}
fragment = TextViewerFragment.newInstance();
}else if( itemType == ListMenuFragment.ITEM_TYPE_IMAGE_VIEWER ) {
if( fragment instanceof ImageViewerFragment == true ) {
return;
}
fragment = ImageViewerFragment.newInstance();
}
// ⑤ 선택된 아이템에 해당하는 프래그먼트를 액티비티 우측에 배치한다.
getFragmentManager().beginTransaction().replace( R.id.viewer_fragment_container, fragment ).commit();
}
});
}
xml에 포함되어 있으면 findViewById()로 인스턴스를 만들 수 있다. 그렇지 않고 동적인 경우면 그냥 인스턴스 만들면 된다.
3. FragmentTransaction
Fragment의 관리는 FragmentManager가 담당한다. 하지만 레이아웃에 Fragment를 추가, 제거, 교체하는데는 꼭 FragmentTransaction을 통하여 수행한다.
뷰의 동작을 transaction으로 관리하는 이유는 효율과 관련이 있다. 예를 들어 A, B fragment가 있는데 A를 생성하고, B를 생성하고 이중 A만 보여주기 위해 B를 숨긴다고 가정하자.
Transaction으로 관리되지 않으면 A를 보여주고, B를 보여주고, B를 숨기고 하는 일련의 과정에 대해 다 화면 업데이트를 해줘야 한다. 하지만 연속적인 화면 업데이트를 따로 하는것은 비 효율적이므로 Transaction으로 묶어 한번에 처리하는 것이다.
실제로 FragmentTransaction 내부에는 큐가 있다.
(주의) FragmentTransaction의 commit() 시점에 대해 주의하자!!
- commit()을 한다고 바로 적용이 되는게 아니다. 예를 들어 다음 코드는 정상 작동하지 않는다.
// ① 프래그먼트를 프래그먼트매니저에 추가한다.
getFragmentManager()
.beginTransaction()
.add(R.id.viewer_fragment_container,textViewerFragment, "TEXT_VIEWER")
.commit();
// ② 프래그먼트매니저에 추가한 프래그먼트를 찾아 참조한다.
TextViewerFragment textViewerFragment = (TextViewerFragment) getFragmentManager().
findFragmentByTag( "TEXT_VIEWER" );
이 코드에서 두번째 TextViewerFragment 는 null 을 리턴한다.
그 이유는 Transaction 처리를 메인스레드의 메시지큐에 추가하기 때문이다. 큐에 유입된 메시지는 메인스레드의 루퍼가 유입된 순서대로 처리한다. 따라서 메인스레드의 루퍼가 프래그먼트 트랜잭션을 처리해줄 때까지 기다려야한다.
참고로 그 시점은.. commit()한 생명주기 함수 다음 부터이다. 예를 들어 onCreate()에서 commit()을 했다면 이후인 onStart()부터 FragmenetManager에서 해당 fragment를 찾을 수 있다.
4. Fragment 제어를 위한 여러 함수들
Fragment를 다루기 위해 여러 함수가 존재하는데, 화면 보기에 비슷하지만 확실히 다르게 동작하기에 주의해야 한다.
- add(int containerViewId, Fragment fragment)
- 프래그먼트 추가. (실제로 fragment를 추가하는 기능은 add 밖에 없다.)
- remove(Fragment fragment)
- 추가된 프래그먼트 삭제. FragmentManager의 관리 목록에서 완전 지운다.
- replace(int containerViewId, Fragment fragment)
- 추가된 프래그먼트를 새로운 프래그먼트와 교체(내부적으로 remove -> add 가 순차적으로 호출)
- detach(Fragment fragment)
- 추가된 프래그먼트의 레이아웃을 떼어냄 (fragment자체는 유지하면서 레이아웃만 지우는 방식이다.)
- attach(Fragment fragment)
- detach 함수로 떼어낸 프래그먼트 레이아웃을 다시 붙임 (이전 fragment의 상태는 유지하면서 화면을 다시 그리게 된다. 참고로 attach는 add와 다르다. 반드시 add 이후 detach된 fragment에 대해서만 attach가 가능하다)
- hide(Fragment fragment)
- 프래그먼트의 레이아웃을 숨김 (fragment와 layout 모두 유지하면서 단순히 레이아웃을 숨기는 것이다.)
- show(Fragment fragment)
- hide 함수로 숨겨진 프래그먼트 레이아웃을 다시 보여줌.
- setRetainInstance(boolean retain)
- 화면전환과 같이 Activity 다시 create 될때 fragment 인스턴스를 그대로 유지할지 여부이다. 설정되고 나면 Activity가 재생성될때의 프레그먼트의 lifecycle 메서드 콜백이 달라진다.
onDetach()
는 호출되지만onDestroy()
는 호출되지 않는다.- 액티비티가 재생성될때
onCreate()
는 호출되지 않는다. - 하지만
onAttach()
,onActivityCreated()
는 호출된다.
각 함수별로 기능이 다르기 때문에 제대로 확인하고 써야 한다.
5. Fragment의 ID와 TAG
fragment를 다루려면 상황에 따라 FragmentManger가 관리하는 특정 fragment의 instance를 가져와야 하는데 원하는 fragment를 찾는데 id와 tag를 이용한다.
id는 일반적으로 xml에 명시된 @+id/~~~~ 이다. 정적으로 레이아웃에 포함된 fragment는 이렇게 ID를 가질 수 있으므로 다음 함수로 찾을 수 있다.
FragmentManger.findFragmentById(int id)
하지만 동적으로 추가되는 fragment는 별도의 ID를 가질 수 없다. 아니, id를 가질 수 있는데, fragment가 포함된 컨테이너의 ID를 가진다.(예를 들어 FrameLayout의 ID)
문제는 해당 컨테이너에 여러개의 Fragment가 포함되었다면 모두 같은 ID를 가져 구분을 할 수 없다. (물론 instanceOf로 찾을수는 있지만..)
이럴 때 TAG를 이용한다. add() 함수를 보면 parameter로 Tag를 줄 수 있는데. 이 때 등록한 Tag를 다음 함수로 찾을 수 있다.
FragmentManager.findFragmentByTag(String tag)
6. Fragment의 BackStack
ActivityGroup을 이용하면 backstack를 가질 수 없지만 fragment는 FragmentManagr에 의해 backstack를 가질 수 있다. (FragmentManager 내부에 별도의 backStack을 가지고 있다. 백스택에 등등록된 하나의 트랜잭션은 backStackRecord라 불린다.)
Fragment 백스택의 단위는 Transaction 실행 단위이다. Fragment를 사용한다고 무조건 backStack를 사용할 수 있는것은 아니며 FragmentTransaction의 다음 함수를 사용해서 스택을 등록한다.
FragmentTransaction.addToBackStack(String tag)
예를 들어 다음과 같다.
getFragmentManager()
.beginTransaction()
.hide( imageViewerFragment )
.show( textViewerFragment )
// 백스택을 위해 해당 트랜잭션을 저장한다.
.addToBackStack( "TEXT_VIEWER_BACKSTACK" )
.commit();
BackStack에 등록된 후 “뒤로가기”를 하면 등록된 Transaction의 반대 동작을 한다. 각 함수별 반대 동작은 아래와 같다.
- add() <-> remove()
- attach() <-> detach()
- show() <-> hide()
BackStatck에서 하나의 BackStackRecord를 꺼내는 동작은 popBackStack()를 통해 할 수 있다. 뒤로가기 버튼도 이걸 호출하는 것이다. 꺼낼때 특정 record를 꺼낼 수도 있는데, 이 때 parameter는 addToBackStack()를 할때 입력했던 이름이다. 참고로 별도로 pop하는 backStackRecord의 순서를 조작할 일이 없다면 add할때 이름을 null로 줘도 된다.
6.1 BackStack 사용시 주의할점
BackStack의 단위가 Transaction이라는 점을 유의해야 한다. 이걸 잘못 사용하면 예상하지 못한 결과가 나타난다.
예를 들어 다음과 같은 경우..
- A fragment를 숨기고 B fragment를 보여준다.
- 한번더 실행 되어 마찬가지로 A fragment를 숨기고 B fragment 를 보여준다.
이 경우 화면은 그냥 B가 그대로 보이는 형태가 됨. 이 상태에서 뒤로가기를 누르면?? 정상적인 동작은 한번 뒤로가기 누르면 그대로 B가 보여지고, 한번 더 뒤로가기를 누르면 A가 보여져야 한다.
하지만 실제로는 2번째 transaction의 반대 동작이 A를 보여주고, B를 숨기는 것이기 때문에 예상했던것과 다르게 B가 보여지는게 아니라 A가 보여진다.
즉, 예상한 뒤로가기 동작은 B > A 이지만 실제로는 A > A 가 된다.
이러한 실수를 방지하기 위해서는 Fragment의 현재 상태를 확인해야 할 필요가 있다. 다음과 같이 확인이 가능하다.
if( itemType == ListMenuFragment.ITEM_TYPE_TEXT_VIEWER && textViewerFragment.isVisible() == false ) {
getFragmentManager()
.beginTransaction()
.hide( imageViewerFragment )
.show( textViewerFragment )
.addToBackStack( "TEXT_VIEWER_BACKSTACK" )
.commit();
}
isVisible()과 같이 Fragmenet의 상태를 확인하는 다양한 함수가 있다.
6.2 BackStackChangeListener
BackStack에 변화가 일어날때를 감지하기 위한 BackStackChangeListener가 존재한다. 필요하면 다음과 같이 쓴다.
getFragmentManager().addOnBackStackChangedListener(this);
getFragmentManager().removeOnBackStackChangedListener(this);
7. Fragment 의 LifeCycle
lifeCycle | 설명 |
---|---|
onAttach | 프래그먼트가 액티비티 레이아웃에 추가될 때 호출된다. 그리고 이 함수가 호출되었다는 것은 프래그먼트매니저가 해당 프래그먼트의 관리를 시작한다는 의미다. |
onCreate | 프래그먼트 객체가 최초 생성될 때 호출된다. 따라서 이 함수에서 프래그먼트 내부에 필요한 각종 클래스들을 생성 및 초기화하면 된다. 그리고 이 함수가 호출되었다는 것은 프래그먼트 객체가 새로 생성됨을 의미한다. 반대로 호출되지 않았다면 기존에 프래그먼트 객체가 존재하여 재사용된 것이다. 일반적으로 프래그먼트를 add하면 호출 되어야 하지만 기존 백스택에 해당 프래그먼트가 존재하면 호출되지 않는다. |
onCreateView | 액티비티에서 프래그먼트의 레이아웃을 요청할 때 호출된다. 따라서 해당 함수에서 프래그먼트 레이아웃을 생성하여 반환해야 한다. 그리고 이 함수가 호출되었다는 것은 프래그먼트의 레이아웃이 새로 생성됨을 의미한다. |
onActivityCreated | 프래그먼트를 포함하고 있는 액티비티 onCreate 생명주기 함수가 호출된 후 호출된다. 따라서 액티비티의 onCreate 과정 이후에 처리해야할 일이 있다면 이 함수에서 처리하면 된다. |
onStart | 프래그먼트 레이아웃이 화면에 표시될 때 호출된다. 단 아직 사용자와 상호작용은 할 수 없는 상태다. |
onResume | 사용자와 상호작용도 할 수 있는 상태다. |
onPause | 사용자와 상호작용을 할 수 없는 상태다. |
onStop | 프래그먼트 레이아웃이 화면에서 사라질 때 호출된다. |
onDestroyView | 액티비티에서 프래그먼트의 레이아웃을 제거할 때 호출된다. 따라서 해당 함수가 호출되면 화면에는 프래그먼트 레이아웃이 존재하지 않는다. |
onDestory | 프래그먼트 객체가 소멸될 때 호출되는 함수다. |
onDetach | 프래그먼트가 액티비티 레이아웃에서 제거될 때 호출된다. 그리고 이 함수가 호출되었다는 것은 프래그먼트매니저가 더 이상 해당 프래그먼트를 관리하지 않는다는 의미다. |
간단히 말하면.. Fragment라는게 원래 완벽히 분리된 뷰의 개념으로 볼 수 있다. 완벽히 분리가 가능하려면 스스로 상태를 관리할 수 있어야 하기에 생명주기가 있다. 그리고 이 생명주기는 결국 이 뷰를 관리할 액티비티에 영향을 받기 마련이다.
Fragment는 add상태, attach상태, show상태 등 액티비티보다 상태가 다양하기 때문에 좀더 많은 생명주기 함수가 존재한다. 하지만 결국 기본 개념은 뷰 자체로의 생명주기를 가지고 있고, 이 뷰가 포함된 액티비티의 생명주기에 영향을 받는다는 점은 다르지 않다.
물론 일반적인 View 는 생명주기를 따로 관리하지 않기 때문에 이렇게 생명주기를 Activity와 함께 맞춰줘야 하는 Fragment의 사용은 좀 더 어려운 측면이 있다.
7.1. 중요한 항목만 정리
- Activity와 연계되어 Fragment가 생성될때는 Activity 라이프라이클이 먼저 호출되고 Fragment 라이프사이클이 호출된다.
(Activity의 onStart() -> Fragment의 onStart(), Activity의 onResume() -> Fragment의 onResume() )
하지만 제거될때는 Fragment의 라이프사이클이 먼저 호출된다.
(Fragment의 onPause() -> Activity의 onPause(), Fragment의 onStop() -> Activity의 onStop())
-
Activity가 동작중일때 Fragment를 add하거나 remove하면 Activity의 라이프사이클 변화 없이 Fragment 라이프사이클만 변화한다. 위 그림의 프레그먼트 add, remove 이다.
-
만약 Fragment를 add하거나 remove할때 백스텍에 추가한다면 위 그림에서 프레그먼트 attach, detach와 같아진다.
백스택에 추가하는 것이 fragment 자체를 없애는 것이 아니기 때문이다. -
Activity, Fragment가 동작중일때 Fragment를 hide 시키면 생명주기 변화가 없다. 단,
onHiddenChange()
메서드가 호출된다.
8. Fragment의 상태 복원
생명주기가 존재하는 Activity에서 상태를 복원하기 위해 Bundle savedInstanceState가 사용되는데, Fragment역시 마찬가지다.
Fragment 생명주기에서 onPuase()뒤에 무조건 onSavedInstanceState(Bundle outState)가 호출된다. 여기에 저장된 Bundle값은 Fragment 생명주기 3군대에서 호출된다. 3군대에 호출되는 내용은 모두 동일한데 개발자가 적당한 위치에서 가져다 쓰라는 것이다.
- onCreate(Bundle savedInstanceState)
- onCreateView(Bundle savedInstanceState)
- onActivityCreated(Bundle savedInstanceState)
그리고 Activity도 마찬가지지만 저장하는 Bundle값은 시스템 프로세스에 저장한다. 상태 복원이라는것 자체가 이미 기존 상태를 잃어버린다는 것이기 때문에 해당 프로세스에 값을 보관할 수 없다. 다른 프로세스에 저장을 한다는 것은 바인더 통신을 한다는 의미다. 따라서 Bundle에 저장할 수 있는 값은 프리미티브 타입이거나 직렬화가 가능한 객체여야 한다.
8.1. Fragment의 추가시 주의해야 할 점과 savedInstanceState의 활용
보통 Activity의 onCreate()에서 Fragment를 add()하는데.. 이 때 주의할 점이 있다. 바로 화면 회전과 같이 onCreate()를 다시 탈때 Fragment를 중복으로 add()할 수도 있다는 점이다. 이를 방지하기 위해 Activity의 savedInstatnceState()를 활용한다.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
// savedInstanceState 번들이 널이라는 것은 해당 앱이 강제 종료 후
// 재시작된 것이 아니라는 의미다. 따라서 이 경우 화면 좌측의 텍스트뷰어
// 프래그먼트를 추가해준다.
// 참고로 만일 savedInstanceState 번들이 널이 아니라면 강제 종료 후
// 재시작하는 과정이므로 텍스트뷰어 프래그먼트를 추가하면 안된다.
// 그 이유는 프래그먼트매니저가 자동으로 해당 프래그먼트를 생성 후 추가해주기
// 때문이다.
if( savedInstanceState == null ){
getFragmentManager()
.beginTransaction()
.add( R.id.viewer_fragment_container, mTextViewerFragment, "TEXT_VIEWER")
.commit();
}
9. Fragment 생성 시 주의할점
Fragment 생성시 넘겨야 하는 파라미터가 있을때 아래와 같은 방법들을 사용할 수 있을것이다.
- 팩토리 메서드를 사용하는 방법
MyFragment.newInstance(parameter);
class MyFragment {
public static MyFragment newInstance(String parameter){
MyFragment instance = new MyFragment();
Bundle bundle = new Bundle();
bundle.putExtra("PARAMETER"M parameter);
instance.setArguments(bundle);
return instance;
}
}
- 생성자를 사용하거나 setter를 사용하는 방법
// 생성자를 통해 전달하는 방법
MyFragment myFragment = new MyFragment(parameter);
// setter 메서드를 사용하는 방법
MyFragment myFragment = new MyFragment();
myFragment.setParameter(parameter);
class MyFragment {
private String mParam;
public MyFragment(String parameter){
this.mParam = parameter;
}
public void setParameter(String parameter){
this.mParam = parameter;
}
}
위 내용중 2번째 방법(생성자를 이용, setter를 이용)은 조심해야 한다.
메모리 부족 시 LMK(Low Memory Killer)에 의해 Activity가 강제종료 될수 있는데 이후 재생성될때 해당 파라미터를 찾을 수 없기 때문이다.
물론 onSavedInstanceState()를 통해 명시적으로 저장할 수 있지만 애초에 Bundle에 저장해서 넘길 경우 굳이 onSavedInstanceState()에서 전달받은 파라미터를 저장하지 않아도 된다.
(당연히… 상태가 변한다면 변경된 값을 저장해야 겠지만..)
또 주의할 점은.. Fragment가 자동으로 재생성될때는 default 생성자가 호출된다.
만약 위의 예처럼 파라미터 없는 default 생성자가 없을 경우 런타임 에러가 발생한다.
아래는 구글 가이드에 적혀있는 내용이다.
All subclasses of Fragment must include a public empty constructor. The framework will often re-instantiate a fragment class when needed, in particular during state restore, and needs to be able to find this constructor to instantiate it. If the empty constructor is not available, a runtime exception will occur in some cases during state restore.
10. android-support-v4 라이브러리
Fragment 는 API 레벨이 11 이상이면 사용이 가능하지만 그 이하는 지원하지 않는다. 11 이상일때는 Activity에 이미 Fragment를 다루는 기능이 있지만 그 이상을 지원하기 위해서는 support-v4 라이브러리를 이용한다. support-v4 라이브러리에서는 Activity가 아니라 FragmentActivity를 상속받아 사용하여야 한다.
댓글남기기