안드로이드 Must be called from main thread of fragment host
안녕하세요. 개발자 드리머즈입니다.
안드로이드 개발 중에 맞닥뜨린 이슈 정리합니다.
문제 현상
어플리케이션 실행 중에 아래와 같은 에러 메시지를 출력하면서 크래쉬가 발생했습니다.
java.lang.IllegalStateException: Must be called from main thread of fragment host
에러가 발생한 코드 위치는 아래와 같습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public class MyFragment extends Fragment{ ..생략.. private Callback myCallback= new Callback() { @Override public void onResponse(Call call, Response response) throws IOException { getActivity().onBackPressed(); } }; } | cs |
Fragmentd에서 뒤로가기 버튼을 누른 것과 동일한 효과를 주기위해 getActivity().onBackPressed()함수를 호출하였는데 여기서 크래쉬가 발생했습니다.
문제 원인
에러 메세지를 해석하면 문제가 되는 코드 getActivity().onBackPressed()는 fragment host(=Activity)의 메인 쓰레드(=UI 쓰레드)에서 호출되야 한다고 합니다. 그런데 문제가 되는 코드가 액티비티가 아닌 Fragment의 쓰레드에서 호출되어 크래쉬가 발생했습니다.
*참고로 디버깅을 통해 확인해보니 아래의 안드로이드 프레임워크 코드에서 쓰레드 체크하고 문제가 있으면 크래쉬를 발생시킵니다.
FragmentManager.java(안드로이드 프레임워크 소스)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | /** * Broken out from exec*, this prepares for gathering and executing operations. * * @param allowStateLoss true if state loss should be ignored or false if it should be * checked. */ private void ensureExecReady(boolean allowStateLoss) { if (mExecutingActions) { throw new IllegalStateException("FragmentManager is already executing transactions"); } if (Looper.myLooper() != mHost.getHandler().getLooper()) { throw new IllegalStateException("Must be called from main thread of fragment host"); } if (!allowStateLoss) { checkStateLoss(); } if (mTmpRecords == null) { mTmpRecords = new ArrayList<>(); mTmpIsPop = new ArrayList<>(); } mExecutingActions = true; try { executePostponedTransaction(null, null); } finally { mExecutingActions = false; } } | cs |
해결 방법
특정 코드가 메인 쓰레드(UI 쓰레드)에서 실행되게 하려면 아래 코드와 같이 runOnUiThread() 함수로 감싸주면 됩니다. 이렇게 하면 getActivity().onBackPressed()함수가 메인 쓰레드에서 실행되어 더이상 크래쉬가 발생하지 않습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public class MyFragment extends Fragment{ ..생략.. private Callback myCallback= new Callback() { @Override public void onResponse(Call call, Response response) throws IOException { getActivity().runOnUiThread(new Runnable() { @Override public void run() { getActivity().onBackPressed(); } }); } }; } | cs |
댓글 영역