首页 > 解决方案 > RxJava在处理后一直在发射项目,如何正确处理?

问题描述

我试图在 ViewModel 中处理 RxJava 一次性。我很确定在 ViewModel 的 onClear 方法中清除一次性用品是一种很好的做法,因此它不会泄漏 ViewModel 本身。但是,当出现 UnknownHostException 时,就在 dispose 操作发生之前,它会导致 ViewModel 泄漏。我打开了泄漏金丝雀,我说与 onError 相关的东西已经泄漏了。

我发现 UnknownHostException 实际上是在 RxJava 的全局错误处理程序下捕获的,所以为什么这个异常一直引用 onError 回调并导致泄漏,即使我处理了一次性。

用例.kt

abstract class UseCase {

    protected var lastDisposable: Disposable? = null
    private val compositeDisposable: CompositeDisposable = CompositeDisposable()

    fun disposeLast() {
        lastDisposable?.let {
            if (!it.isDisposed) {
                it.dispose()
            }
        }
    }

    fun dispose() {
        compositeDisposable.clear()
    }

    fun Disposable.addDisposable() {
        compositeDisposable.add(this)
    }

    class None()
}

FlowableUseCase.kt

abstract class FlowableUseCase<in Params, Model> @Inject constructor(private val flowableRxTransformer: FlowableRxTransformer<Model>) :
    UseCase() {

    internal abstract fun buildUseCaseFlowable(params: Params): Flowable<Model>

    operator fun invoke(
        params: Params,
        onLoading: () -> Unit = {},
        onError: ((error: Throwable) -> Unit) = {},
        onSuccess: ((entity: Model) -> Unit) = {},
        onFinished: () -> Unit = {}
    ) {
        onLoading()
        disposeLast()
        lastDisposable = buildUseCaseFlowable(params)
            .compose(flowableRxTransformer)
            .doAfterTerminate(onFinished)
            .subscribe(onSuccess, onError) // leaks happened here

        lastDisposable?.addDisposable()
    }
}

如果我尝试删除 onError 回调,则不再有错误。所以我很确定它会导致内存泄漏,但这个操作可能会导致 OnErrorNotImplementedException,所以我不想这样做。

任何形式的建议都是可以接受的。提前致谢。

泄漏金丝雀头部转储

┬───
│ GC Root: System class
│
├─ android.provider.FontsContract class
│    Leaking: NO (App↓ is not leaking and a class is never leaking)
│    ↓ static FontsContract.sContext
├─ com.potatocandie.cleanprayertime.App instance
│    Leaking: NO (Application is a singleton)
│    mBase instance of android.app.ContextImpl, not wrapping known Android
│    context
│    ↓ App.componentManager
│          ~~~~~~~~~~~~~~~~
├─ dagger.hilt.android.internal.managers.ApplicationComponentManager instance
│    Leaking: UNKNOWN
│    Retaining 40 bytes in 3 objects
│    ↓ ApplicationComponentManager.component
│                                  ~~~~~~~~~
├─ com.potatocandie.cleanprayertime.DaggerApp_HiltComponents_SingletonC instance
│    Leaking: UNKNOWN
│    Retaining 7731 bytes in 279 objects
│    ↓ DaggerApp_HiltComponents_SingletonC.okHttpClientBuilder
│                                          ~~~~~~~~~~~~~~~~~~~
├─ okhttp3.OkHttpClient$Builder instance
│    Leaking: UNKNOWN
│    Retaining 203 bytes in 4 objects
│    ↓ OkHttpClient$Builder.dispatcher
│                           ~~~~~~~~~~
├─ okhttp3.Dispatcher instance
│    Leaking: UNKNOWN
│    Retaining 288 bytes in 7 objects
│    ↓ Dispatcher.runningAsyncCalls
│                 ~~~~~~~~~~~~~~~~~
├─ java.util.ArrayDeque instance
│    Leaking: UNKNOWN
│    Retaining 84 bytes in 2 objects
│    ↓ ArrayDeque.elements
│                 ~~~~~~~~
├─ java.lang.Object[] array
│    Leaking: UNKNOWN
│    Retaining 64 bytes in 1 objects
│    ↓ Object[].[0]
│               ~~~
├─ okhttp3.internal.connection.RealCall$AsyncCall instance
│    Leaking: UNKNOWN
│    Retaining 3815 bytes in 123 objects
│    ↓ RealCall$AsyncCall.responseCallback
│                         ~~~~~~~~~~~~~~~~
├─ retrofit2.OkHttpCall$1 instance
│    Leaking: UNKNOWN
│    Retaining 3795 bytes in 122 objects
│    Anonymous class implementing okhttp3.Callback
│    ↓ OkHttpCall$1.val$callback
│                   ~~~~~~~~~~~~
├─ retrofit2.adapter.rxjava3.CallEnqueueObservable$CallCallback instance
│    Leaking: UNKNOWN
│    Retaining 3705 bytes in 119 objects
│    ↓ CallEnqueueObservable$CallCallback.observer
│                                         ~~~~~~~~
├─ retrofit2.adapter.rxjava3.BodyObservable$BodyObserver instance
│    Leaking: UNKNOWN
│    Retaining 3687 bytes in 118 objects
│    ↓ BodyObservable$BodyObserver.observer
│                                  ~~~~~~~~
├─ io.reactivex.rxjava3.internal.operators.flowable.
│  FlowableFromObservable$SubscriberObserver instance
│    Leaking: UNKNOWN
│    Retaining 3674 bytes in 117 objects
│    ↓ FlowableFromObservable$SubscriberObserver.downstream
│                                                ~~~~~~~~~~
├─ io.reactivex.rxjava3.internal.operators.flowable.
│  FlowableOnBackpressureLatest$BackpressureLatestSubscriber instance
│    Leaking: UNKNOWN
│    Retaining 3658 bytes in 116 objects
│    ↓ FlowableOnBackpressureLatest$BackpressureLatestSubscriber.downstream
│                                                                ~~~~~~~~~~
├─ io.reactivex.rxjava3.internal.operators.flowable.FlowableMap$MapSubscriber
│  instance
│    Leaking: UNKNOWN
│    Retaining 3596 bytes in 113 objects
│    ↓ FlowableMap$MapSubscriber.downstream
│                                ~~~~~~~~~~
├─ io.reactivex.rxjava3.internal.operators.flowable.
│  FlowableDoOnEach$DoOnEachSubscriber instance
│    Leaking: UNKNOWN
│    Retaining 3564 bytes in 112 objects
│    ↓ FlowableDoOnEach$DoOnEachSubscriber.downstream
│                                          ~~~~~~~~~~
├─ io.reactivex.rxjava3.internal.operators.flowable.
│  FlowableRetryWhen$RetryWhenSubscriber instance
│    Leaking: UNKNOWN
│    Retaining 3400 bytes in 105 objects
│    ↓ FlowableRetryWhen$RetryWhenSubscriber.downstream
│                                            ~~~~~~~~~~
├─ io.reactivex.rxjava3.subscribers.SerializedSubscriber instance
│    Leaking: UNKNOWN
│    Retaining 2860 bytes in 79 objects
│    ↓ SerializedSubscriber.downstream
│                           ~~~~~~~~~~
├─ io.reactivex.rxjava3.internal.operators.flowable.
│  FlowableDoOnEach$DoOnEachSubscriber instance
│    Leaking: UNKNOWN
│    Retaining 2837 bytes in 78 objects
│    ↓ FlowableDoOnEach$DoOnEachSubscriber.downstream
│                                          ~~~~~~~~~~
├─ io.reactivex.rxjava3.internal.operators.flowable.
│  FlowableFlatMap$InnerSubscriber instance
│    Leaking: UNKNOWN
│    Retaining 2781 bytes in 76 objects
│    ↓ FlowableFlatMap$InnerSubscriber.parent
│                                      ~~~~~~
├─ io.reactivex.rxjava3.internal.operators.flowable.
│  FlowableFlatMap$MergeSubscriber instance
│    Leaking: UNKNOWN
│    Retaining 2732 bytes in 75 objects
│    ↓ FlowableFlatMap$MergeSubscriber.downstream
│                                      ~~~~~~~~~~
├─ io.reactivex.rxjava3.internal.operators.flowable.FlowableMap$MapSubscriber
│  instance
│    Leaking: UNKNOWN
│    Retaining 1837 bytes in 58 objects
│    ↓ FlowableMap$MapSubscriber.downstream
│                                ~~~~~~~~~~
├─ io.reactivex.rxjava3.internal.operators.flowable.
│  FlowableDoOnEach$DoOnEachSubscriber instance
│    Leaking: UNKNOWN
│    Retaining 1793 bytes in 56 objects
│    ↓ FlowableDoOnEach$DoOnEachSubscriber.onNext
│                                          ~~~~~~
├─ com.potatocandie.domain.base.FlowableUseCase$invoke$5 instance
│    Leaking: UNKNOWN
│    Retaining 12 bytes in 1 objects
│    Anonymous class implementing io.reactivex.rxjava3.functions.Consumer
│    ↓ FlowableUseCase$invoke$5.this$0
│                               ~~~~~~
├─ com.potatocandie.domain.usecases.GetCurrentMonthsPrayerTimesUseCase instance
│    Leaking: UNKNOWN
│    Retaining 45 bytes in 3 objects
│    ↓ GetCurrentMonthsPrayerTimesUseCase.lastDisposable
│                                         ~~~~~~~~~~~~~~
├─ io.reactivex.rxjava3.internal.subscribers.LambdaSubscriber instance
│    Leaking: UNKNOWN
│    Retaining 800 bytes in 34 objects
│    ↓ LambdaSubscriber.onError
│                       ~~~~~~~
├─ com.potatocandie.domain.base.
│  FlowableUseCase$sam$io_reactivex_rxjava3_functions_Consumer$0 instance
│    Leaking: UNKNOWN
│    Retaining 760 bytes in 32 objects
│    Anonymous class implementing io.reactivex.rxjava3.functions.Consumer
│    ↓ FlowableUseCase$sam$io_reactivex_rxjava3_functions_Consumer$0.function
│                                                                    ~~~~~~~~
├─ com.potatocandie.cleanprayertime.features.today.
│  PrayerTimesViewModel$getCurrentMonthsPrayerTimes$2 instance
│    Leaking: UNKNOWN
│    Retaining 748 bytes in 31 objects
│    Anonymous subclass of kotlin.jvm.internal.Lambda
│    ↓ PrayerTimesViewModel$getCurrentMonthsPrayerTimes$2.this$0
│                                                         ~~~~~~
╰→ com.potatocandie.cleanprayertime.features.today.PrayerTimesViewModel instance
​     Leaking: YES (ObjectWatcher was watching this because com.potatocandie.
​     cleanprayertime.features.today.PrayerTimesViewModel received
​     ViewModel#onCleared() callback)
​     Retaining 732 bytes in 30 objects
​     key = 1a238931-2884-4446-a5c2-66d46390134f
​     watchDurationMillis = 7824
​     retainedDurationMillis = 2822

标签: kotlinmvvmretrofitrx-java

解决方案


推荐阅读