subscribe 결과는 사용되지 않습니다


133

오늘 Android Studio 3.1로 업그레이드했는데 보푸라기 검사가 몇 개 더 추가 된 것 같습니다. 이러한 보푸라기 점검 중 하나 subscribe()는 변수에 저장되지 않은 원샷 RxJava2 호출에 대한 것입니다. 예를 들어, 내 Room 데이터베이스에서 모든 플레이어 목록을 가져옵니다.

Single.just(db)
            .subscribeOn(Schedulers.io())
            .subscribe(db -> db.playerDao().getAll());

큰 노란색 블록과이 툴팁이 생성됩니다.

의 결과 subscribe는 사용되지 않습니다

Android Studio의 스크린 샷.  툴팁과 함께 코드가 노란색으로 강조 표시됩니다.  툴팁 텍스트 : 가입 결과가 사용되지 않습니다.

이와 같은 원샷 Rx 호출에 대한 모범 사례는 무엇입니까? Disposabledispose()를 계속 유지해야합니까 ? 아니면 그냥 계속해야 @SuppressLint합니까?

이것은 RxJava2 ( io.reactivex) 에만 영향을 미치는 것으로 보이며 RxJava ( rx)에는이 보푸라기가 없습니다.


두 솔루션 중 솔직히 @SuppressLint가 최선이 아니라고 생각합니다. 어쩌면 내가 틀렸지 만 실제로 코드가 IDE 경고 및 힌트를 변경해서는 안된다고 생각합니다.
Arthur Attout

@ArthurAttout Agreed, 현재 Disposable멤버 범위를 유지 dispose()하고 싱글이 완료되면 전화를 걸지만 불필요하게 번거로운 것 같습니다. 이 작업을 수행하는 더 좋은 방법이 있는지 알고 싶습니다.
Michael Dodd

8
RxJava 스트림이 Activity / Fragment / ViewModel 내에서 구독되지 않으면이 보푸라기 경고가 성가신 것으로 생각합니다. 액티비티 라이프 사이클을 고려하지 않고 안전하게 실행할 수있는 Completable이 있지만 여전히 폐기해야합니까?
EM

RxLifecycle 고려
최봉재

답변:


122

IDE는 구독이 폐기되지 않을 때 구독에 미칠 수있는 영향을 알지 못하므로 잠재적으로 안전하지 않은 것으로 취급합니다. 예를 들어, Single네트워크 호출이 포함되어있을 수 있습니다. 네트워크 호출 Activity은 실행 중에 메모리가 누출 될 수 있습니다 .

많은 양의 Disposables 를 관리하는 편리한 방법 은 CompositeDisposable 을 사용하는 것입니다 . 엔 CompositeDisposable클로징 클래스에서 새 인스턴스 변수를 만든 다음 모든 Disposables를 CompositeDisposable에 추가하십시오 (RxKotlin을 사용하면 addTo(compositeDisposable)모든 Disposables에 추가 할 수 있음 ). 마지막으로 인스턴스를 완료하면을 호출하십시오 compositeDisposable.dispose().

그러면 보푸라기 경고 Disposables가 사라지고 제대로 관리됩니다.

이 경우 코드는 다음과 같습니다.

CompositeDisposable compositeDisposable = new CompositeDisposable();

Disposable disposable = Single.just(db)
        .subscribeOn(Schedulers.io())
        .subscribe(db -> db.get(1)));

compositeDisposable.add(disposable); //IDE is satisfied that the Disposable is being managed. 
disposable.addTo(compositeDisposable); //Alternatively, use this RxKotlin extension function.


compositeDisposable.dispose(); //Placed wherever we'd like to dispose our Disposables (i.e. in onDestroy()).

error: cannot find symbol method addTo(CompositeDisposable)"rxjava : 2.1.13" 에서 컴파일 오류가 발생 합니다. 이 방법은 어디에서 왔습니까? (RxSwift 또는 RxKotlin으로 가정)
aeracode

2
예, RxKotlin 방식입니다.
urgentx

1
유동성의 경우 무엇을해야합니다
헌트

우리는 doOnSubscribe이 무엇을하고 있다면
킬러

2
메모리 누수가 발생하지 않습니다. 네트워크 호출이 완료되고 onComplete가 호출되면 가비지 수집은 일회용을 명시 적으로 참조하지 않고 폐기하지 않는 한 나머지를 처리합니다.
Gabriel Vasconcelos

26

활동이 파괴되는 순간, 일회용품 목록이 지워지고 우리는 좋습니다.

io.reactivex.disposables.CompositeDisposable mDisposable;

    mDisposable = new CompositeDisposable();

    mDisposable.add(
            Single.just(db)
                    .subscribeOn(Schedulers.io())
                    .subscribe(db -> db.get(1)));

    mDisposable.dispose(); // dispose wherever is required

9

DisposableSingleObserver 를 구독 할 수 있습니다 :

Single.just(db)
    .subscribeOn(Schedulers.io())
    .subscribe(new DisposableSingleObserver<Object>() {
            @Override
            public void onSuccess(Object obj) {
                // work with the resulting todos...
                dispose();
            }

            @Override
            public void onError(Throwable e) {
                // handle the error case...
                dispose();
            }});

Single객체 를 직접 처리해야하는 경우 (예 : 객체가 방출되기 전에) 참조 onSubscribe(Disposable d)를 가져 와서 사용하는 메소드 를 구현할 수 있습니다 Disposable.

당신은 또한 SingleObserver당신 자신의 인터페이스를 실현 하거나 다른 자식 클래스를 사용할 수 있습니다.


5

제안했듯이 전역 CompositeDisposable작업을 사용하여 구독 작업 결과를 추가 할 수 있습니다 .

RxJava2Extensions의 라이브러리로부터 자동 생성 일회용 제거하는 유용한 방법을 포함 CompositeDisposable가 완료 될 때를. subscribeAutoDispose 섹션을 참조하십시오 .

귀하의 경우에는 다음과 같이 보일 수 있습니다

SingleConsumers.subscribeAutoDispose(
    Single.just(db)
            .subscribeOn(Schedulers.io()),
    composite,
    db -> db.playerDao().getAll())

2

Uber AutoDispose 및 rxjava를 사용할 수 있습니다.as

        Single.just(db)
            .subscribeOn(Schedulers.io())
            .as(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(this)))
            .subscribe(db -> db.playerDao().getAll());

ScopeProvider를 기반으로 구독을 취소 할 때 이해해야합니다.


라이프 사이클 제공자가 사용 가능한 것으로 가정합니다. 또한 "as"방법은 불안정한 것으로 표시되므로이를 사용하면 Lint 경고가 발생합니다.
Dabbler

1
@Dabbler에게 감사드립니다. 이 .as 방법은 RxJava 2.1.7까지 실험이었고, 2.2에 그것의 안정.
blaffie

1

나는 구독을 올바르게 처리하는 방법과 특히이 게시물에 대한 질문으로 다시 돌아 오는 것을 발견했습니다. 몇몇 블로그와 회담에서는 전화를하지 않으면 dispose반드시 메모리 누수가 발생한다고 주장합니다. 내 이해에 따르면, 결과를 저장하지 않는다는 보푸라기 경고는 subscribe다음과 같은 이유로 문제가 아닙니다.

  • 모든 관찰 가능 항목이 Android 활동의 컨텍스트에서 실행되는 것은 아닙니다.
  • 옵저버 블은 동기식 일 수 있습니다
  • 관찰이 완료되면 처리를 암시 적으로 호출합니다.

보푸라기 경고를 억제하고 싶지 않기 때문에 최근에 동기식 관찰 가능한 경우에 다음 패턴을 사용하기 시작했습니다.

var disposable: Disposable? = null

disposable = Observable
   .just(/* Whatever */)
   .anyOperator()
   .anyOtherOperator()
   .subscribe(
      { /* onSuccess */ },
      { /* onError */ },
      {
         // onComplete
         // Make lint happy. It's already disposed because the stream completed.
         disposable?.dispose()
      }
   )

정확성을 확인하거나 허점을 발견했는지 여부에 관계없이 이에 대한 의견에 관심이 있습니다.


0

Disposables를 수동으로 사용하지 않는 다른 방법이 있습니다 (구독 추가 및 제거).

Observable을 정의 할 수 있으며 ObservableSubjectBehaviour (RxJava를 사용하는 경우) 에서 컨텐츠를 수신하게됩니다 . 그리고 관찰 가능한 것을 LiveData 에 전달 하면 작동합니다. 초기 질문을 바탕으로 다음 예를 확인하십시오.

private val playerSubject: Subject<Player> = BehaviorSubject.create()

private fun getPlayer(idPlayer: String) {
        playerSubject.onNext(idPlayer)
}

private val playerSuccessful: Observable<DataResult<Player>> = playerSubject
                        .flatMap { playerId ->
                            playerRepository.getPlayer(playerId).toObservable()
                        }
                        .share()

val playerFound: LiveData<Player>
    get() = playerSuccessful
        .filterAndMapDataSuccess()
        .toLiveData()

val playerNotFound: LiveData<Unit>
    get() = playerSuccessful.filterAndMapDataFailure()
        .map { Unit }
        .toLiveData()

// These are a couple of helpful extensions

fun <T> Observable<DataResult<T>>.filterAndMapDataSuccess(): Observable<T> =
filter { it is DataResult.Success }.map { (it as DataResult.Success).data }

fun <T> Observable<DataResult<T>>.filterAndMapDataFailure(): Observable<DataResult.Failure<T>> =
filter { it is DataResult.Failure }.map { it as DataResult.Failure<T> }

-10

doOnSubscribe () 연산자를 사용하여 일회용을 올바르게 처리했다고 확신한다면 Gradle에 추가 할 수 있습니다.

android {
lintOptions {
     disable 'CheckResult'
}}

10
이렇게하면 검사되지 않은 결과의 모든 인스턴스에 대한이 보푸라기 검사가 억제됩니다. OP의 예를 제외하고 누군가가 반환 된 결과를 처리 해야하는 경우가 많이 있습니다. 이것은 망치를 사용하여 파리를 죽이고 있습니다.
tir38

16
이러지 마십시오! 이러한 경고가 표시되는 이유가 있습니다. 당신이하고있는 일을 알고 있고 구독을 처분 할 필요가 없다는 것을 알고 있다면 @SuppressLint("CheckResult")방법으로 억제 할 수 있습니다 .
빅터 렌 디나
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.