안드로이드 android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

안녕하세요. 개발자 드리머즈입니다.


안드로이드 개발 중에 이슈가 발생해 정리합니다.

문제 현상



어플을 실행시키면 아래의 에러가 발생하면서 어플이 종료됐습니다.

1
2
3
4
5
6
7
FATAL EXCEPTION: OkHttp Dispatcher
Process: com.dreamaz.xxxxxx, PID: 9639
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
    at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6891)
    at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1048)
    at android.view.View.requestLayout(View.java:19781)
    ..생략...
cs


코드상에서 에러가 발생한 위치는 아래의 코드에서 빨간색으로 표시한 부분입니다.

mRecyclerView.setAdapter(mAdapter);


1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class FragmentA extends Fragment {
    ..생략..
    private Callback callback= new Callback() {
        ..생략..
 
        @Override
        public void onResponse(Call call, Response response) throws IOException {
            ..생략..
 
            mAdapter = new UserAdapter(userInfo);
            mRecyclerView.setAdapter(mAdapter);
        }
    };
}
cs

원인

안드로이드에선 TextView, ImageView와 같은 View의 UI와 관련된 일은 메인 쓰레드(=UI 쓰레드)에서만 허용하고 있습니다. 그런데 위의 코드에서 파란색 음영으로 표시한 부분을 보면 Callback입니다. 여기에 있는 코드는 메인 쓰레드가 아닌 다른 쓰레드에서 실행이 됩니다. 그리고 mRecyclerView.setAdapter(mAdapter);에서mRecyclerView는 RecyclerView의 객체입니다. 당연한 이야기지만 RecyclerView는 View를 상속받았기에 View입니다.




결국 문제가 되는 코드는 메인 쓰레드가 아닌 다른 쓰레드에서 View를 조작하기에 안드로이드 정책에 따라 에러가 발생하게 됩니다.


해결 방법

문제가 되는 코드를 메인 쓰레드(UI 쓰레드)에서 실행되게 하면 됩니다.

아래와 같이 runOnUiThread() 함수를 사용하고 이 안에 View를 조작하는 코드를 넣으면 됩니다.


저의 경우에 문제가 발생한 코드는 액티비티 클래스가 아니라 프래그먼트 클래스였기에 프래그먼트에서 액티비티에 접근하기 위해 getActivity() 함수가 추가로 사용됐다는 점을 주의하세요. 프래그먼트가 아닌 일반 액티비티에서 문제가 발생했다면 아래의 빨간색 코드에서 getActivity().만 빼면 됩니다.


참고 : https://developer.android.com/reference/android/app/Activity.html#runOnUiThread(java.lang.Runnable)


1
2
3
4
5
6
7
8
9
10
11
12
@Override
public void onResponse(Call call, Response response) throws IOException {
    ..생략..
 
    mAdapter = new UserAdapter(userInfo);
    getActivity().runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        mRecyclerView.setAdapter(mAdapter);
                                    }
                                });
}
cs


작성자

Posted by 드리머즈

관련 글

댓글 영역