ไม่ได้ใช้ผลลัพธ์ของการสมัครสมาชิก


136

วันนี้ฉันอัปเกรดเป็น Android Studio 3.1 แล้วซึ่งดูเหมือนว่าจะมีการตรวจสอบผ้าสำลีเพิ่มขึ้นอีกเล็กน้อย หนึ่งในการตรวจสอบขุยเหล่านี้มีไว้สำหรับการsubscribe()เรียกRxJava2 แบบ one-shot ที่ไม่ได้เก็บไว้ในตัวแปร ตัวอย่างเช่นการรับรายชื่อผู้เล่นทั้งหมดจากฐานข้อมูลห้องของฉัน:

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

ผลลัพธ์เป็นบล็อกสีเหลืองขนาดใหญ่และคำแนะนำเครื่องมือนี้:

subscribeไม่ได้ใช้ผลลัพธ์ของ

ภาพหน้าจอของ Android Studio  รหัสถูกเน้นด้วยสีเหลืองพร้อมคำแนะนำเครื่องมือ  ข้อความคำแนะนำเครื่องมือ: ไม่ได้ใช้ผลลัพธ์ของการสมัครสมาชิก

แนวทางปฏิบัติที่ดีที่สุดสำหรับการโทรแบบ one-shot Rx เช่นนี้คืออะไร? ฉันควรถือDisposableและdispose()ทำให้เสร็จหรือไม่? หรือฉันควรจะ@SuppressLintเดินต่อไป?

ดูเหมือนว่าจะมีผลเฉพาะ RxJava2 ( io.reactivex), RxJava ( rx) ไม่มีผ้าสำลีนี้


จากทั้งสองวิธีของคุณฉันคิดว่า @SuppressLint ไม่ใช่วิธีที่ดีที่สุด บางทีฉันอาจจะผิด แต่ฉันคิดว่ารหัสไม่ควรเปลี่ยนคำเตือนและ / หรือคำแนะนำของ IDE
Arthur Attout

@ArthurAttout เห็นด้วยตอนนี้ฉันอยู่Disposableในขอบเขตของสมาชิกและโทรหาdispose()เมื่อซิงเกิ้ลเสร็จสมบูรณ์ แต่ดูเหมือนว่าจะยุ่งยากโดยไม่จำเป็น ฉันสนใจที่จะดูว่ามีวิธีที่ดีกว่านี้หรือไม่
Michael Dodd

8
ฉันคิดว่าคำเตือนผ้าสำลีนี้น่ารำคาญเมื่อสตรีม RxJava ไม่ได้สมัครสมาชิกจากภายใน Activity / Fragment / ViewModel ฉันมี Completable ที่สามารถทำงานได้อย่างปลอดภัยโดยไม่คำนึงถึงวงจรชีวิตของกิจกรรม แต่ฉันยังจำเป็นต้องกำจัดทิ้งหรือไม่
EM

พิจารณา RxLifecycle
최봉재

คำตอบ:


126

IDE ไม่ทราบว่าการสมัครสมาชิกของคุณจะมีผลกระทบใดบ้างเมื่อไม่ได้รับการกำจัดดังนั้นจึงถือว่าการสมัครรับข้อมูลอาจไม่ปลอดภัย ตัวอย่างเช่นคุณSingleอาจมีการโทรผ่านเครือข่ายซึ่งอาจทำให้หน่วยความจำรั่วไหลหากคุณActivityถูกละทิ้งระหว่างการดำเนินการ

วิธีที่สะดวกในการจัดการจำนวนมากDisposableคือการใช้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
ด่วน x

1
จะทำอย่างไรในกรณีที่ไหลได้
ล่า

จะเกิดอะไรขึ้นถ้าเราทำสิ่งนี้ใน doOnSubscribe
Killer

2
มันจะไม่ทำให้หน่วยความจำรั่วไหล เมื่อการโทรผ่านเครือข่ายเสร็จสิ้นและมีการเรียก onComplete การรวบรวมขยะจะจัดการกับส่วนที่เหลือเว้นแต่คุณจะเก็บข้อมูลอ้างอิงที่ชัดเจนเกี่ยวกับสิ่งที่ใช้แล้วทิ้งและอย่ากำจัดทิ้ง
Gabriel Vasconcelos

26

ทันทีที่กิจกรรมจะถูกทำลายรายชื่อ Disposables จะถูกล้างและเราก็พร้อม

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

11

คุณสามารถสมัครสมาชิกด้วย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

ตามที่แนะนำไว้คุณอาจใช้ global 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


สิ่งนี้ถือว่าผู้ให้บริการวงจรชีวิตพร้อมใช้งาน นอกจากนี้เมธอด "เป็น" ยังถูกทำเครื่องหมายว่าไม่เสถียรดังนั้นการใช้จะส่งผลให้มีคำเตือน Lint
Dabbler

1
ขอบคุณ @Dabbler เห็นด้วย ้นวิธีการคือการทดลองจน RxJava 2.1.7 และ 2.2 มันมีเสถียรภาพ
blaffie

0

ครั้งแล้วครั้งเล่าฉันพบว่าตัวเองกลับมาที่คำถามเกี่ยวกับวิธีการกำจัดการสมัครสมาชิกอย่างถูกต้องและโดยเฉพาะอย่างยิ่งสำหรับการโพสต์นี้ บล็อกและการพูดคุยหลายแห่งอ้างว่าการโทรไม่สำเร็จdisposeจำเป็นต้องนำไปสู่การรั่วไหลของหน่วยความจำซึ่งฉันคิดว่าเป็นข้อความที่กว้างเกินไป ตามความเข้าใจของฉันคำเตือนผ้าสำลีเกี่ยวกับการไม่เก็บผลลัพธ์ของsubscribeมันเป็นปัญหาที่ไม่ใช่ในบางกรณีเนื่องจาก:

  • สิ่งที่สังเกตได้ทั้งหมดไม่ได้ทำงานในบริบทของกิจกรรม Android
  • สามารถสังเกตได้แบบซิงโครนัส
  • Dispose เรียกว่าโดยปริยายหากการสังเกตเสร็จสมบูรณ์

เนื่องจากฉันไม่ต้องการระงับคำเตือนผ้าสำลีฉันเพิ่งเริ่มใช้รูปแบบต่อไปนี้สำหรับกรณีที่มีการสังเกตแบบซิงโครนัส:

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และสิ่งที่สังเกตได้นั้นจะได้รับเนื้อหาจากSubjectBehaviour (ในกรณีที่คุณใช้ 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> }

-11

หากคุณแน่ใจว่าใช้แล้วทิ้งได้รับการจัดการอย่างถูกต้องเช่นใช้ตัวดำเนินการ doOnSubscribe () คุณสามารถเพิ่มสิ่งนี้ลงใน Gradle:

android {
lintOptions {
     disable 'CheckResult'
}}

10
การดำเนินการนี้จะระงับการตรวจสอบผ้าสำลีสำหรับอินสแตนซ์ทั้งหมดของผลลัพธ์ที่ไม่เลือก มีหลายครั้งนอกตัวอย่างของ OP ที่ใครบางคนควรจัดการกับผลลัพธ์ที่ส่งคืน นี่คือการใช้ค้อนขนาดใหญ่เพื่อฆ่าแมลงวัน
tir38

16
โปรดอย่าทำแบบนี้! มีเหตุผลที่คุณจะได้รับคำเตือนเหล่านี้ หากคุณรู้ว่าคุณกำลังทำอะไรอยู่ (และรู้ว่าคุณไม่จำเป็นต้องยกเลิกการสมัครรับข้อมูลของคุณเลย) คุณสามารถระงับได้ด้วย@SuppressLint("CheckResult")วิธีการ
Victor Rendina
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.