[Android] Fragment의 getActivity()는 언제 null을 반환할까
Fragment의 getActivity()는 언제 null을 반환할까 (When do fragment.getActivity() return null)
Fragment의 onCreateView 내부에서 getActivity()가 Null을 반환하여 앱이 죽는 현상이 발생했다.
그런데 테스트 환경에서는 Null일때도 있고, 아닐때도 있어 재현이 어려웠는데,
이번 기회에 getActivity()가 언제 null을 반환하는지 알아보려 한다.
먼저 해당 프래그먼트는 다음과 같이 activity의 xml에서 선언되었다.
위의 LifeCycle을 참조하여 각 페이즈마다 getActivity가 Null인지 아닌지를 체크할 것이다.
위 코드를 이용하여 null여부를 로그에 기록한다.
결과는?
생명주기 내부에서는 getActivity는 null이 되지 않았다.
생명주기를 잘못 사용한 문제는 아닌 것 같다.
그렇다면 이 오류는 왜 나는 것일까?
activity는 어떤 경우에 null이 되는 것일까?
당연하게도 Fragment가 attach되기 전과 destroyed된 후는 null이 된다.
1. Fragment가 Destroy되고 난 후
Fragment의 생명주기 내에서 비동기로 작업을 진행하고(네트워크 요청 등) 해당 응답에 따라 ui에 표시하는 동작이 포함되어 있을 때
작업이 완료되기 전 사용자가 뒤로가기를 눌러 액티비티를 꺼버리면, 자연스럽게 Fragment는 Destroy되고 getActivity값은 null이 된다.
위 코드에서는 val response:response<MatchUp>... 에서 네트워크 요청 후 응답을 기다리고
해당 응답에 따라 UI를 제어하는 부분인데
당연히 응답이 끝나기 전에 사용자가 Activity를 종료해버리면 activity!!.runOnUiThread에서 NullPointerException이 발생한다
해결법은 단순하다.
activity가 null이면 해당 동작을 하지 않게 만들면 된다.
(java의 경우 activiy.runOnUiThread위에 if(activity!= null)을 입력하면 된다)
2. Fragment가 Add되기 전
xml에서 Fragment를 추가하지 않고
코드 내부에서 동적으로 getFragmentManager().beginTransaction().add(...).commit()을 통해 추가하는 경우에도 발생할 수 있다.
//...
ExampleFragment fragmenet= new ExampleFragment();
getFragmentManager().beginTransaction().add(R.id.example_container, fragment).commit();
fragment.useActivity();
/...
다음과 같은 코드가 있다고 해보자
(useActivity는 getActivity()를 사용하는 메소드라고 가정하자)
이 경우 fragment.useActivity() 내부의 getActivity()는 Null일수도, 아닐수도 있다.
안드로이드 개발자 문서에 명시되어 있듯이 commit()은 항상 즉시 트랜젝션을 실행하지는 않는다.
따라서 해당 동작을 할 때 트랜젝션이 처리되지 않은 상태로 다음줄로 넘어갔다면
fragment가 add되기 전 상태이므로 getActivity()가 null이 되고
처리된 경우에는 null이 아니게 된다.
https://developer.android.com/guide/components/fragments?hl=ko
//...
ExampleFragment fragmenet= new ExampleFragment();
getFragmentManager().beginTransaction().add(R.id.example_container, fragment).commitNow();
fragment.useActivity();
/...
반드시 즉시 처리되어야하는 transaction이라면
commit()대신 commitNow()를 사용하고,
//...
ExampleFragment fragmenet= new ExampleFragment();
Runnable onCommit= ()->{
fragment.useActivity();
}
getFragmentManager().beginTransaction().add(R.id.example_container, fragment).runOnCommit(onCommit).commit();
/...
해당 transaction이 처리되고 나서 진행되어야만 하는(의존성이 있는) 작업이 있다면
runOnCommit(new Runnnable(){...})을 사용하면 된다.
++추가
반드시 runOnCommit(new Runnable(){...}) 한 뒤 commit()해 주어야 커밋된다.
runOnCommit(new Runnable(){...}).commit()