안드로이드 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(nullnull);
        } 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


작성자

Posted by 드리머즈

관련 글

댓글 영역