Android 内存泄漏:实现io.reactivex.Observer的匿名类
我正在做一个Android项目。我的一些活动正在扩展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
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