我正在开发一个 Android 项目。我的一些活动正在扩展SiteFinderActivity
。此类负责验证当前会话并通过一些抽象函数将其传递给其子级。
我使用JakeWharton 的 RxRelay将 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))
}
}
活动调用validateToken()
并侦听其中继以获取结果。
abstract class SiteFinderActivity : AppCompatActivity() {
abstract fun onAuthenticationError()
abstract fun onAzureAccessTokenReceived(azureAccessToken: String)
private var azureAuthVM: AuthViewModel? = getAzureAuthVM()
private var authObserver: Observer<AuthState>? = createDefaultObserver(logger) {
val weakThis = WeakReference(this@SiteFinderActivity)
if (weakThis.get() == null) return@createDefaultObserver
when (this) {
is AuthOnSuccessToken -> {
//...
onAzureAccessTokenReceived(azureAccessToken)
}
else -> {
onAuthenticationError()
}
}
}
override fun onStart() {
super.onStart()
azureAuthVM?.relay?.safeSubscribe(authObserver)
}
override fun onStop() {
super.onStop()
// We need to nullify it here otherwise it leaks the context
azureAuthVM = null
authObserver = 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)
}
}
最后,这是 Leakcanary 为我记录的内容。它表明 888 正在泄漏。我阅读了许多文档并查看了 Stackoverflow 上的许多答案,但无法确切地意识到问题出在哪里。
====================================
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
====================================