Zaktualizowałem dzisiaj do Android Studio 3.1, który wydaje się mieć kilka dodatkowych sprawdzeń kłaczków. Jedno z tych sprawdzeń lintów dotyczy jednorazowych subscribe()
wywołań RxJava2 , które nie są przechowywane w zmiennej. Na przykład, pobieranie listy wszystkich graczy z mojej bazy danych Pokoju:
Single.just(db)
.subscribeOn(Schedulers.io())
.subscribe(db -> db.playerDao().getAll());
W rezultacie powstaje duży żółty blok i ta podpowiedź:
Wynik
subscribe
nie jest używany
Jaka jest najlepsza praktyka w przypadku takich jednorazowych wywołań Rx? Czy powinienem zachować kompletność Disposable
i dispose()
na? A może powinienem po prostu @SuppressLint
iść dalej?
Wydaje się, że dotyczy to tylko RxJava2 ( io.reactivex
), RxJava ( rx
) nie ma tego linta .
android
android-studio
rx-java2
lint
android-studio-3.1
Michael Dodd
źródło
źródło
Disposable
zakres członków at i wzywam,dispose()
gdy singiel się zakończy, ale wydaje się to niepotrzebnie kłopotliwe. Jestem zainteresowany, aby sprawdzić, czy są na to lepsze sposoby.Odpowiedzi:
Środowisko IDE nie wie, jakie potencjalne skutki może mieć Twoja subskrypcja, gdy nie zostanie wyrzucona, więc traktuje ją jako potencjalnie niebezpieczną. Na przykład
Single
może zawierać wywołanie sieciowe, które może spowodować wyciek pamięci, jeśliActivity
zostanie porzucone podczas wykonywania.Wygodnym sposobem zarządzania dużą liczbą
Disposable
s jest użycie CompositeDisposable ; po prostu utwórz nowąCompositeDisposable
zmienną instancji w klasie otaczającej, a następnie dodaj wszystkie swoje produkty jednorazowe do CompositeDisposable (za pomocą RxKotlin możesz po prostu dołączyćaddTo(compositeDisposable)
do wszystkich swoich produktów jednorazowych). Wreszcie, kiedy skończysz z instancją, zadzwońcompositeDisposable.dispose()
.Pozwoli to usunąć ostrzeżenia o kłaczkach i zapewni
Disposables
prawidłowe zarządzanie.W tym przypadku kod wyglądałby tak:
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()).
źródło
error: cannot find symbol method addTo(CompositeDisposable)
z „rxjava: 2.1.13”. Skąd ta metoda? (Przypuszczam, że RxSwift lub RxKotlin)W momencie zniszczenia Aktywności lista rzeczy jednorazowego użytku zostaje wyczyszczona i wszystko jest w porządku.
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
źródło
Możesz subskrybować za pomocą 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(); }});
W przypadku, gdy chcesz bezpośrednio pozbyć się
Single
obiektu (np. Zanim wyemituje), możesz zaimplementować metodę,onSubscribe(Disposable d)
aby uzyskać i użyćDisposable
referencji.Możesz również zrealizować
SingleObserver
interfejs samodzielnie lub użyć innych klas podrzędnych.źródło
Jak sugerowano, możesz użyć globalnego,
CompositeDisposable
aby dodać tam wynik operacji subskrypcji.RxJava2Extensions biblioteka zawiera użyteczne sposoby, aby automatycznie usunąć utworzone jednorazowe od
CompositeDisposable
kiedy to kończy. Zobacz sekcję subscribeAutoDispose .W twoim przypadku może to wyglądać tak
SingleConsumers.subscribeAutoDispose( Single.just(db) .subscribeOn(Schedulers.io()), composite, db -> db.playerDao().getAll())
źródło
Możesz użyć Uber AutoDispose i rxjava
.as
Single.just(db) .subscribeOn(Schedulers.io()) .as(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(this))) .subscribe(db -> db.playerDao().getAll());
Upewnij się, że rozumiesz, kiedy rezygnujesz z subskrypcji na podstawie ScopeProvider.
źródło
Ciągle wracam do pytania, jak prawidłowo pozbyć się subskrypcji, aw szczególności do tego postu. Kilka blogów i wykładów twierdzi, że nieudane wezwanie
dispose
prowadzi do wycieku pamięci, co moim zdaniem jest zbyt ogólne. W moim rozumieniu ostrzeżenie osubscribe
nieprzechowywaniu wyniku w niektórych przypadkach nie stanowi problemu, ponieważ:Ponieważ nie chcę tłumić ostrzeżeń o kłaczkach, ostatnio zacząłem używać następującego wzorca dla przypadków z obserwowalnym synchronicznym:
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() } )
Byłbym zainteresowany wszelkimi komentarzami na ten temat, niezależnie od tego, czy jest to potwierdzenie poprawności, czy odkrycie luki.
źródło
Istnieje inny sposób, który polega na unikaniu ręcznego używania produktów jednorazowych (dodawanie i usuwanie subskrypcji).
Możesz zdefiniować Observable, który otrzyma zawartość z SubjectBehaviour (w przypadku korzystania z RxJava). I przekazując to obserwowalne do LiveData , powinno to działać. Sprawdź następny przykład oparty na pierwszym pytaniu:
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> }
źródło
Jeśli jesteś pewien, że disposable obsługiwane poprawnie, na przykład za pomocą operatora doOnSubscribe (), możesz dodać to do Gradle:
android { lintOptions { disable 'CheckResult' }}
źródło
@SuppressLint("CheckResult")
metodę.