Closes #19921: Update appcompat and fragment dependencies to 1.3.x
parent
7dbfca2d2e
commit
74c1cc82fb
@ -1,59 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.perf
|
||||
|
||||
import androidx.lifecycle.LifecycleCoroutineScope
|
||||
import androidx.navigation.NavController
|
||||
import java.util.WeakHashMap
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import org.mozilla.fenix.R
|
||||
|
||||
/**
|
||||
* This class asynchronously loads the navigation graph XML. This is a performance optimization:
|
||||
* large nav graphs take ~29ms to inflate on the Moto G5 (#16900). This also seemingly prevents the
|
||||
* HomeFragment layout XML from being unnecessarily inflated when it isn't used, improving perf by
|
||||
* ~148ms on the Moto G5 for VIEW start up (#18245) though it was unintentional and we may wish to
|
||||
* implement more intentional for that.
|
||||
*
|
||||
* This class is defined as an Object, rather than as a class instance in our Components, because
|
||||
* it needs to be called by the [NavController] extension function which can't easily access Components.
|
||||
*
|
||||
* To use this class properly, [inflateNavGraphAsync] must be called first before
|
||||
* [blockForNavGraphInflation] using the same [NavController] instance.
|
||||
*/
|
||||
object NavGraphProvider {
|
||||
|
||||
// We want to store member state on the NavController. However, there is no way to do this.
|
||||
// Instead, we store state as part of a map: NavController instance -> State. In order to
|
||||
// garbage collect our data when the NavController is no longer relevant, we use a WeakHashMap.
|
||||
private val map = WeakHashMap<NavController, Job>()
|
||||
|
||||
fun inflateNavGraphAsync(navController: NavController, lifecycleScope: LifecycleCoroutineScope) {
|
||||
val inflationJob = lifecycleScope.launch(Dispatchers.IO) {
|
||||
val inflater = navController.navInflater
|
||||
navController.graph = inflater.inflate(R.navigation.nav_graph)
|
||||
}
|
||||
|
||||
map[navController] = inflationJob
|
||||
}
|
||||
|
||||
/**
|
||||
* The job should block the main thread if it isn't completed so that the NavGraph can be loaded
|
||||
* before any navigation is done.
|
||||
*
|
||||
* [inflateNavGraphAsync] must be called before this method.
|
||||
*
|
||||
* @throws IllegalStateException if [inflateNavGraphAsync] wasn't called first for this [NavController]
|
||||
*/
|
||||
fun blockForNavGraphInflation(navController: NavController) {
|
||||
val inflationJob = map[navController] ?: throw IllegalStateException("Expected " +
|
||||
"`NavGraphProvider.inflateNavGraphAsync` to be called before this method with the same " +
|
||||
"`NavController` instance. If this occurred in a test, you probably need to add the " +
|
||||
"DisableNavGraphProviderAssertionRule.")
|
||||
runBlockingIncrement { inflationJob.join() }
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.helpers
|
||||
|
||||
import io.mockk.every
|
||||
import io.mockk.mockkObject
|
||||
import io.mockk.unmockkObject
|
||||
import org.junit.rules.TestWatcher
|
||||
import org.junit.runner.Description
|
||||
import org.mozilla.fenix.perf.NavGraphProvider
|
||||
|
||||
/**
|
||||
* Disables the call order assertions defined by the [NavGraphProvider] for use in testing.
|
||||
* This is necessary because unit tests generally don't follow the application lifecycle and thus
|
||||
* call the methods out of order, causing an assertion to be thrown unexpectedly. You may need to
|
||||
* apply this rule if you see the following exception in your test:
|
||||
*
|
||||
* Unfortunately, JUnit 4 discourages setting test state globally so we apply this to each test that
|
||||
* has the failure rather than disabling it globally.
|
||||
*/
|
||||
class DisableNavGraphProviderAssertionRule : TestWatcher() {
|
||||
|
||||
// public for code reuse.
|
||||
fun setUp() {
|
||||
mockkObject(NavGraphProvider)
|
||||
every { NavGraphProvider.blockForNavGraphInflation(any()) } returns Unit
|
||||
}
|
||||
|
||||
// public for code reuse.
|
||||
fun tearDown() { //
|
||||
unmockkObject(NavGraphProvider)
|
||||
}
|
||||
|
||||
override fun starting(description: Description?) {
|
||||
setUp()
|
||||
}
|
||||
|
||||
override fun finished(description: Description?) {
|
||||
tearDown()
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.detektrules.perf
|
||||
|
||||
import io.gitlab.arturbosch.detekt.api.CodeSmell
|
||||
import io.gitlab.arturbosch.detekt.api.Config
|
||||
import io.gitlab.arturbosch.detekt.api.Debt
|
||||
import io.gitlab.arturbosch.detekt.api.Entity
|
||||
import io.gitlab.arturbosch.detekt.api.Issue
|
||||
import io.gitlab.arturbosch.detekt.api.Rule
|
||||
import io.gitlab.arturbosch.detekt.api.Severity
|
||||
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||
|
||||
private const val VIOLATION_MSG = "If you named a method `navigate`, please rename it to something a bit" +
|
||||
"more specific such as `navigateTo...`. However, if you are trying to invoke the Android NavController.navigate" +
|
||||
"please use `org.mozilla.fenix.ext.NavController.navigateBlockingForAsyncNavGraph`" +
|
||||
"instead. Because using `navigate` directly in the code can lead to the NavGraph not being loaded" +
|
||||
"since it relies on a blocking call done in `navigateBlockingForAsyncNavGraph`."
|
||||
|
||||
/**
|
||||
* A check to prevent the use of `navController.navigate`. Since the NavGraph is loaded dynamically
|
||||
* and asynchronously, there is a check in place to make sure the graph is loaded by wrapping the
|
||||
* `navigate` function. However, using it directly might lead to code that needs the NavGraph being
|
||||
* called before it being inflated.
|
||||
*/
|
||||
class MozillaNavigateCheck(config: Config = Config.empty) : Rule(config) {
|
||||
|
||||
override val issue = Issue(
|
||||
"MozillaNavigateCheck",
|
||||
Severity.Performance,
|
||||
"Prevents us from calling `navController.navigate` instead of the functions that" +
|
||||
"wrap it",
|
||||
Debt.TEN_MINS
|
||||
)
|
||||
|
||||
|
||||
override fun visitCallExpression(expression: KtCallExpression) {
|
||||
super.visitCallExpression(expression)
|
||||
|
||||
val calledMethod = expression.calleeExpression?.firstChild?.node?.chars
|
||||
|
||||
//We check for the navigate method and we have to ignore our extension function file, since
|
||||
//we call navigate there
|
||||
if (calledMethod == "navigate" ) {
|
||||
report(CodeSmell(issue, Entity.from(expression), VIOLATION_MSG))
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue