안드로이드 Must be called from main thread of fragment host
프로그래밍/Android 관련2018. 3. 11. 20:40
안녕하세요. 개발자 드리머즈입니다.
안드로이드 개발 중에 맞닥뜨린 이슈 정리합니다.
문제 현상
어플리케이션 실행 중에 아래와 같은 에러 메시지를 출력하면서 크래쉬가 발생했습니다.
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 |
댓글 영역