-
Notifications
You must be signed in to change notification settings - Fork 101
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #894 from square/ray/leaky-backhandler
`View.backPressHandler` memory leak fix.
- Loading branch information
Showing
5 changed files
with
205 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
117 changes: 117 additions & 0 deletions
117
...-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/BackPressedHandlerTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
package com.squareup.workflow1.ui | ||
|
||
import android.view.View | ||
import android.view.ViewGroup | ||
import androidx.activity.ComponentActivity | ||
import androidx.activity.OnBackPressedCallback | ||
import androidx.activity.OnBackPressedDispatcherSpy | ||
import androidx.lifecycle.Lifecycle.State.DESTROYED | ||
import androidx.lifecycle.Lifecycle.State.RESUMED | ||
import androidx.lifecycle.LifecycleRegistry | ||
import androidx.lifecycle.ViewTreeLifecycleOwner | ||
import androidx.test.ext.junit.rules.ActivityScenarioRule | ||
import com.google.common.truth.Truth.assertThat | ||
import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess | ||
import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule | ||
import org.junit.Rule | ||
import org.junit.Test | ||
import org.junit.rules.RuleChain | ||
|
||
@OptIn(WorkflowUiExperimentalApi::class) | ||
internal class BackPressedHandlerTest { | ||
private val scenarioRule = ActivityScenarioRule(ComponentActivity::class.java) | ||
private val scenario get() = scenarioRule.scenario | ||
|
||
@get:Rule val rules: RuleChain = RuleChain.outerRule(DetectLeaksAfterTestSuccess()) | ||
.around(scenarioRule) | ||
.around(IdlingDispatcherRule) | ||
|
||
private var viewHandlerCount = 0 | ||
private val viewBackHandler: BackPressHandler = { | ||
viewHandlerCount++ | ||
} | ||
|
||
@Test fun itWorksWhenHandlerIsAddedBeforeAttach() { | ||
scenario.onActivity { activity -> | ||
val view = View(activity) | ||
view.backPressedHandler = viewBackHandler | ||
|
||
activity.setContentView(view) | ||
assertThat(viewHandlerCount).isEqualTo(0) | ||
|
||
activity.onBackPressed() | ||
assertThat(viewHandlerCount).isEqualTo(1) | ||
} | ||
} | ||
|
||
@Test fun itWorksWhenHandlerIsAddedAfterAttach() { | ||
scenario.onActivity { activity -> | ||
val view = View(activity) | ||
activity.setContentView(view) | ||
|
||
view.backPressedHandler = viewBackHandler | ||
assertThat(viewHandlerCount).isEqualTo(0) | ||
|
||
activity.onBackPressed() | ||
assertThat(viewHandlerCount).isEqualTo(1) | ||
} | ||
} | ||
|
||
@Test fun onlyActiveWhileViewIsAttached() { | ||
var fallbackCallCount = 0 | ||
val defaultBackHandler = object : OnBackPressedCallback(true) { | ||
override fun handleOnBackPressed() { | ||
fallbackCallCount++ | ||
} | ||
} | ||
|
||
scenario.onActivity { activity -> | ||
activity.onBackPressedDispatcher.addCallback(defaultBackHandler) | ||
|
||
val view = View(activity) | ||
view.backPressedHandler = viewBackHandler | ||
|
||
activity.onBackPressed() | ||
assertThat(fallbackCallCount).isEqualTo(1) | ||
assertThat(viewHandlerCount).isEqualTo(0) | ||
|
||
activity.setContentView(view) | ||
activity.onBackPressed() | ||
assertThat(fallbackCallCount).isEqualTo(1) | ||
assertThat(viewHandlerCount).isEqualTo(1) | ||
|
||
(view.parent as ViewGroup).removeView(view) | ||
activity.onBackPressed() | ||
assertThat(fallbackCallCount).isEqualTo(2) | ||
assertThat(viewHandlerCount).isEqualTo(1) | ||
|
||
activity.setContentView(view) | ||
activity.onBackPressed() | ||
assertThat(fallbackCallCount).isEqualTo(2) | ||
assertThat(viewHandlerCount).isEqualTo(2) | ||
} | ||
} | ||
|
||
@Test fun callbackIsRemoved() { | ||
scenario.onActivity { activity -> | ||
val spy = OnBackPressedDispatcherSpy(activity.onBackPressedDispatcher) | ||
assertThat(spy.callbacks()).isEmpty() | ||
|
||
val lifecycle = LifecycleRegistry(activity) | ||
lifecycle.currentState = RESUMED | ||
|
||
val view = View(activity) | ||
view.backPressedHandler = viewBackHandler | ||
assertThat(spy.callbacks()).hasSize(1) | ||
|
||
ViewTreeLifecycleOwner.set(view) { lifecycle } | ||
activity.setContentView(view) | ||
|
||
(view.parent as ViewGroup).removeView(view) | ||
assertThat(spy.callbacks()).hasSize(1) | ||
|
||
lifecycle.currentState = DESTROYED | ||
assertThat(spy.callbacks()).isEmpty() | ||
} | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
...re-android/src/androidTest/java/com/squareup/workflow1/ui/OnBackPressedDispatcherSpy.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package androidx.activity; | ||
|
||
import java.util.ArrayDeque; | ||
|
||
public class OnBackPressedDispatcherSpy { | ||
private final OnBackPressedDispatcher dispatcher; | ||
|
||
public OnBackPressedDispatcherSpy(OnBackPressedDispatcher dispatcher) { | ||
this.dispatcher = dispatcher; | ||
} | ||
|
||
public ArrayDeque<OnBackPressedCallback> callbacks() { | ||
return dispatcher.mOnBackPressedCallbacks; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters