투케이2K

344. (TWOK/ERROR) [Android] Cannot invoke setValue on a background thread IllegalStateException 본문

투케이2K 에러관리

344. (TWOK/ERROR) [Android] Cannot invoke setValue on a background thread IllegalStateException

투케이2K 2026. 2. 13. 17:13
728x90
반응형

[환경 설정 및 설명]

프로그램 : Android / 안드로이드

설 명 : [Android] Cannot invoke setValue on a background thread IllegalStateException

 

[설 명]

 

--------------------------------------------------------------------------
[개발 및 테스트 환경]
--------------------------------------------------------------------------

- 제목 : [Android] Cannot invoke setValue on a background thread IllegalStateException


- 테스트 환경 : 삼성 갤럭시 스마트폰 / 안드로이드 / Android / RxJava / LiveData


- 사전) RxJava 설명 : 

  >> Rx (ReactiveX) :

    - ReactiveX 는 관찰가능한 절차를 통해 비동기, 이벤트 기반 프로그램을 구성하기 위한 라이브러리 입니다

    - ReactiveX 는 Observer Pattern 옵저버 패턴 을 확장하며, Sequence 를 조합할 수 있는 연산자를 지원합니다

    - ReactiveX 는 low-level Thread, 동기화, Thread 안전성, non-blocking I/O 에 관한 우려를 줄입니다

  >> RxJava (Reactive java) :

    - Reactive Programming 패러다임을 자바에서 구현한 프로그래밍 라이브러리 입니다

    - RxJava 는 함수형 프로그래밍 방식을 도입했으며, Side Effect 가 없는 순수 함수를 지향하기 때문에 Thread-Safe 합니다

--------------------------------------------------------------------------





--------------------------------------------------------------------------
[에러 원인]
--------------------------------------------------------------------------

1. LiveData/MutableLiveData 의 setValue() 를 메인 (UI) 스레드에서만 호출해야 하는데, 백그라운드 스레드에서 호출되어 발생하는 이슈


2. ❌ 에러 발생 로그 전문 : 

  >> Cannot invoke setValue on a background thread
    java.lang.IllegalStateException: Cannot invoke setValue on a background thread


3. ✅ 참고 추가 설명 : LiveData/MutableLiveData 의 setValue() vs postValue() 차이

  >> setValue(T value)

    - 메인 스레드에서만 호출 가능
    - 즉시 옵저버에 전달

  >> postValue(T value)

    - 어느 스레드에서든 호출 가능
    - 메인 스레드로 전달되어 비동기 업데이트
    - 여러 번 연속 호출 시 마지막 값만 전달될 수 있음 (중간값이 coalesce 될 수 있음)

--------------------------------------------------------------------------





--------------------------------------------------------------------------
[해결 방법]
--------------------------------------------------------------------------

방법 [1] : 백그라운드 스레드 (예: I/O, 네트워크, Room 쿼리, 코루틴 Dispatchers.IO) 에서는 setValue 대신 postValue 를 사용해 데이터 메인 스레드로 전달 처리

  >> setValue() ❌ (금지)
  >> postValue() ✅ (스레드 안전, 내부적으로 메인 스레드로 전달)


방법 [2] : 메인 스레드로 컨텍스트 전환 후 setValue() 호출 처리

  >> 코루틴 withContext(Dispatchers.Main) / viewModelScope.launch(Dispatchers.Main)
  >> Handler(Looper.getMainLooper())
  >> RxJava observeOn(AndroidSchedulers.mainThread())


예시 소스 코드 [RxJava] : Observable 생성 후 subscribe 에서 AndroidSchedulers mainThread 처리 수행

  // -----------------------------------------------
  // [Observable create 사용해 생성 실시 : 생성자]
  // -----------------------------------------------
  // Observable<String> : String 값 onNext 반환 수행
  // -----------------------------------------------

  Observable<String> observable = Observable.create(subscriber -> {
      try {
          if (!subscriber.isDisposed()) { // ✅ [연결된 구독자가 있는 경우]

              // [onNext] 데이터 알림 전달
              subscriber.onNext("SUCCESS");

              // [onCompleted] 완료 알림 전달
              subscriber.onComplete();
          }

      } catch (Exception e) {
          e.printStackTrace();

          // ❌ [onError] 에러 알림 전달
          subscriber.onError(e);
      }
  });

  // -----------------------------------------------



  // -----------------------------------------------
  // [Observable 생성자 를 구독 (Subscribe) 및 데이터 소비 수행]
  // -----------------------------------------------

  Disposable disposable = observable
        .subscribeOn(Schedulers.io()) // (선택) 데이터 생성/업스트림을 백그라운드로 수행
        .observeOn(AndroidSchedulers.mainThread()) // ✅ 구독 콜백을 메인 스레드에서 실행
        .subscribe(
                value -> { // ✅ onNext 응답 처리
                  S_Log.d("KWON_TWOK", ">>>>>>>>>>>>>>>>>>>> [Observable] : [onNext] : " + String.valueOf(value) + " >>>>>>>>>>>>>>>>>>>>");

                  resUpdate.setValue(sendResponseData); // TODO 리턴 반환
                },
                error -> { // ❌ onError 응답 처리
                  S_Log.e("KWON_TWOK", ">>>>>>>>>>>>>>>>>>>> [Observable] : [onError] : " + String.valueOf(error.getMessage()) + " >>>>>>>>>>>>>>>>>>>>");

                  resUpdate.setValue(sendErrorData); // TODO 에러 반환
                },
                () -> { // ✅ onCompleted 응답 처리
                  S_Log.w("KWON_TWOK", ">>>>>>>>>>>>>>>>>>>> [Observable] : [onCompleted] >>>>>>>>>>>>>>>>>>>>");
                }
        );

--------------------------------------------------------------------------





--------------------------------------------------------------------------
[참고 사이트]
--------------------------------------------------------------------------

[Observable] create 사용해 생성자 만들기 및 subscribe 구독 결과 메시지 확인

https://kkh0977.tistory.com/3599

https://blog.naver.com/kkh0977/222970344668?trackingCode=blog_bloghome_searchlist


[Observable] just 사용해 생성자 만들기 및 subscribe 구독 결과 메시지 확인

https://blog.naver.com/kkh0977/222970341475?trackingCode=blog_bloghome_searchlist


[Subject] AsyncSubject 사용해 onNext() 발행 된 마지막 데이터 출력 실시

https://blog.naver.com/kkh0977/222970372557?trackingCode=blog_bloghome_searchlist

--------------------------------------------------------------------------
 
728x90
반응형
Comments