Skip to content

Commit 750b2a2

Browse files
committed
Don't use replay(1) to cache View references
The `replay` operator internally caches N+1 elements instead of just N, meaning it will keep a strong reference to the previous emitted element. When this element references an Activity, leaks occur. See ReactiveX/RxJava#6475.
1 parent ea1112c commit 750b2a2

File tree

2 files changed

+16
-19
lines changed
  • ext/acorn/acorn-rx/src

2 files changed

+16
-19
lines changed

ext/acorn/acorn-rx/src/main/java/com/nhaarman/acorn/presentation/RxScene.kt

+4-19
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ package com.nhaarman.acorn.presentation
1919
import androidx.annotation.CallSuper
2020
import arrow.core.Option
2121
import arrow.core.toOption
22-
import com.nhaarman.acorn.presentation.RxScene.ContainerEvent.Attached
23-
import com.nhaarman.acorn.presentation.RxScene.ContainerEvent.Detached
2422
import com.nhaarman.acorn.state.SceneState
2523
import io.reactivex.Observable
2624
import io.reactivex.disposables.CompositeDisposable
@@ -75,20 +73,13 @@ abstract class RxScene<V : Container>(
7573

7674
private val startedEventsSubject = BehaviorSubject.createDefault(false)
7775

78-
private val containerEventsSubject = BehaviorSubject.createDefault<ContainerEvent<V>>(Detached)
76+
private val viewSubject = BehaviorSubject.createDefault(Option.empty<V>())
7977

8078
/**
8179
* Publishes a stream of optional [V] instances that are attached to this
8280
* Scene.
8381
*/
84-
protected val view: Observable<Option<V>> = containerEventsSubject
85-
.map { event ->
86-
when (event) {
87-
is Attached<V> -> event.v.toOption()
88-
is Detached -> Option.empty()
89-
}
90-
}
91-
.replay(1).autoConnect()
82+
protected val view: Observable<Option<V>> = viewSubject.hide()
9283

9384
@CallSuper
9485
override fun onStart() {
@@ -99,12 +90,12 @@ abstract class RxScene<V : Container>(
9990
@CallSuper
10091
override fun attach(v: V) {
10192
super.attach(v)
102-
containerEventsSubject.onNext(Attached(v))
93+
viewSubject.onNext(v.toOption())
10394
}
10495

10596
@CallSuper
10697
override fun detach(v: V) {
107-
containerEventsSubject.onNext(Detached)
98+
viewSubject.onNext(Option.empty())
10899
super.detach(v)
109100
}
110101

@@ -119,12 +110,6 @@ abstract class RxScene<V : Container>(
119110
sceneDisposables.dispose()
120111
}
121112

122-
@Suppress("unused")
123-
private sealed class ContainerEvent<out V> {
124-
class Attached<V>(val v: V) : ContainerEvent<V>()
125-
object Detached : ContainerEvent<Nothing>()
126-
}
127-
128113
/**
129114
* A utility function to automatically subscribe and dispose of source
130115
* [Observable] instance when this Scene starts and stops.

ext/acorn/acorn-rx/src/test/java/com/nhaarman/acorn/presentation/RxSceneTest.kt

+12
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,18 @@ class RxSceneTest {
5151
expect(observer.lastValue).toBe(Option.just(testView))
5252
}
5353

54+
@Test
55+
fun `subscribing after view attach`() {
56+
/* Given */
57+
scene.attach(testView)
58+
59+
/* When */
60+
val observer = scene.viewObservable.test()
61+
62+
/* Then */
63+
expect(observer.lastValue).toBe(Option.just(testView))
64+
}
65+
5466
@Test
5567
fun `detaching a view notifies observers`() {
5668
/* Given */

0 commit comments

Comments
 (0)