Android 内存泄漏:实现io.reactivex.Observer的匿名类

Android 内存泄漏:实现io.reactivex.Observer的匿名类,android,rx-java2,leakcanary,kotlin-inline-class,Android,Rx Java2,Leakcanary,Kotlin Inline Class,我正在做一个Android项目。我的一些活动正在扩展SiteFinderActivity。此类负责验证当前会话,并通过几个抽象函数将其传递给其子级 我使用以将结果从ViewModels传递给订阅者,在本例中为SiteFinderActivity。这是我的ViewModel的简化版本 sealed class AuthState { object AuthOnError: AuthState() class AuthOnSuccessToken(val accessToken: S

我正在做一个Android项目。我的一些活动正在扩展
SiteFinderActivity
。此类负责验证当前会话,并通过几个抽象函数将其传递给其子级

我使用以将结果从ViewModels传递给订阅者,在本例中为SiteFinderActivity。这是我的ViewModel的简化版本

sealed class AuthState {
    object AuthOnError: AuthState()
    class AuthOnSuccessToken(val accessToken: String): AuthState()
}

class AuthViewModel(
        val context: Context,
        val logger: Logger
) {

    /**
     * Subscribe to this observer in order to be notified when the result is ready.
     */
    val relay: PublishRelay<AuthState> = PublishRelay.create()

    fun validateToken(): {
        // Validation...
        // Once it is done then
        relay.accept(AuthOnSuccessToken(it))
    }
}
因为我在项目中经常使用这种方法,所以我创建了这个实用函数。在我看来,这是内存泄漏的根本原因。该文件位于项目的某个位置(活动之外)


这段代码绝对不正确:

    val weakThis = WeakReference(this@SiteFinderActivity)
    if (weakThis.get() == null) return@createDefaultObserver
因为创建对实例的弱引用并立即检查null是没有意义的。如果弱引用是这里的解决方案,那么应该提前创建它,以便回调只保留对弱引用的引用,而不是对活动的引用

import com.atco.logger.Logger
import io.reactivex.Observer
import io.reactivex.disposables.Disposable

inline fun <T : Any> createDefaultObserver(logger: Logger, crossinline onNext: T.() -> Unit) = object : Observer<T> {
    override fun onComplete() {}

    override fun onSubscribe(d: Disposable) {}

    override fun onNext(t: T) {
        onNext(t)
    }

    override fun onError(e: Throwable) {
        logger.logException(e)
    }
}

尽管如此,我认为弱引用是没有必要的。这里的关键问题是,在onStop中,您应该清除订阅,而不仅仅是将观察者设置为null。

我认为您的代码中存在一个问题,您定义了
onNext
方法,它会立即调用
onNext
。这要么是编译器支持的非常糟糕的编码,要么是引入了无限递归循环。
====================================
    HEAP ANALYSIS RESULT
    ====================================
    1 APPLICATION LEAKS

    References underlined with "~~~" are likely causes.
    Learn more at https://squ.re/leaks.

    204736 bytes retained by leaking objects
    Signature: d477dd791e60b0167ba58d241bd7f6ce875a33d4
    ┬───
    │ GC Root: System class
    │
    ├─ android.provider.FontsContract class
    │    Leaking: NO (SiteFinderApplication↓ is not leaking and a class is never leaking)
    │    ↓ static FontsContract.sContext
    ├─ com.atco.forsite.app.SiteFinderApplication instance
    │    Leaking: NO (SiteFinderApplication↓ is not leaking and Application is a singleton)
    │    SiteFinderApplication does not wrap an activity context
    │    ↓ SiteFinderApplication.shadow$_klass_
    ├─ com.atco.forsite.app.SiteFinderApplication class
    │    Leaking: NO (a class is never leaking)
    │    ↓ static SiteFinderApplication.appComponent
    │                                   ~~~~~~~~~~~~
    ├─ com.atco.forsite.di.DaggerAppComponent instance
    │    Leaking: UNKNOWN
    │    ↓ DaggerAppComponent.provideAzureAuthTokenProvider
    │                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ├─ dagger.internal.DoubleCheck instance
    │    Leaking: UNKNOWN
    │    ↓ DoubleCheck.instance
    │                  ~~~~~~~~
    ├─ com.atco.auth.AuthViewModel instance
    │    Leaking: UNKNOWN
    │    ↓ AuthViewModel.relay
    │                    ~~~~~
    ├─ com.jakewharton.rxrelay2.PublishRelay instance
    │    Leaking: UNKNOWN
    │    ↓ PublishRelay.subscribers
    │                   ~~~~~~~~~~~
    ├─ java.util.concurrent.atomic.AtomicReference instance
    │    Leaking: UNKNOWN
    │    ↓ AtomicReference.value
    │                      ~~~~~
    ├─ com.jakewharton.rxrelay2.PublishRelay$PublishDisposable[] array
    │    Leaking: UNKNOWN
    │    ↓ PublishRelay$PublishDisposable[].[0]
    │                                       ~~~
    ├─ com.jakewharton.rxrelay2.PublishRelay$PublishDisposable instance
    │    Leaking: UNKNOWN
    │    ↓ PublishRelay$PublishDisposable.downstream
    │                                     ~~~~~~~~~~
    ├─ io.reactivex.observers.SafeObserver instance
    │    Leaking: UNKNOWN
    │    ↓ SafeObserver.downstream
    │                   ~~~~~~~~~~
    ├─ com.atco.forsite.app.activity.SiteFinderActivity$$special$$inlined$createDefaultObserver$1 instance
    │    Leaking: UNKNOWN
    │    Anonymous class implementing io.reactivex.Observer
    │    ↓ SiteFinderActivity$$special$$inlined$createDefaultObserver$1.this$0
    │                                                                   ~~~~~~
    ╰→ com.atco.forsite.screens.splash.StartupActivity instance
    ​     Leaking: YES (ObjectWatcher was watching this because com.atco.forsite.screens.splash.StartupActivity received Activity#onDestroy() callback and Activity#mDestroyed is true)
    ​     key = b7c3c771-d399-475a-ab22-a7985eaec020
    ​     watchDurationMillis = 9874
    ​     retainedDurationMillis = 4873
    ====================================
    0 LIBRARY LEAKS

    Library Leaks are leaks coming from the Android Framework or Google libraries.
    ====================================
    METADATA

    Please include this in bug reports and Stack Overflow questions.

    Build.VERSION.SDK_INT: 29
    Build.MANUFACTURER: Google
    LeakCanary version: 2.2
    App process name: com.atco.forsite
    Analysis duration: 13179 ms
    Heap dump file path: /data/user/0/com.atco.forsite/files/leakcanary/2020-03-13_11-58-39_932.hprof
    Heap dump timestamp: 1584122335006
    ====================================
    val weakThis = WeakReference(this@SiteFinderActivity)
    if (weakThis.get() == null) return@createDefaultObserver