Fixes #401: Resolve known leak and install LeakCanary
parent
b4d1ac0d63
commit
2bc769190e
|
@ -145,6 +145,8 @@ dependencies {
|
|||
implementation Deps.mozilla_ui_icons
|
||||
|
||||
implementation Deps.mozilla_lib_crash
|
||||
debugImplementation Deps.leakcanary
|
||||
releaseImplementation Deps.leakcanary_noop
|
||||
|
||||
testImplementation Deps.junit
|
||||
androidTestImplementation Deps.tools_test_runner
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<manifest
|
||||
package="org.mozilla.fenix"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<application
|
||||
tools:replace="android:name"
|
||||
android:name="org.mozilla.fenix.DebugFenixApplication"/>
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,43 @@
|
|||
/* 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
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.squareup.leakcanary.AndroidHeapDumper
|
||||
import com.squareup.leakcanary.HeapDumper
|
||||
import com.squareup.leakcanary.LeakCanary
|
||||
import com.squareup.leakcanary.internal.LeakCanaryInternals
|
||||
import org.mozilla.fenix.R.string.pref_key_leakcanary
|
||||
import org.mozilla.fenix.ext.getPreferenceKey
|
||||
import java.io.File
|
||||
|
||||
class DebugFenixApplication : FenixApplication() {
|
||||
|
||||
private var heapDumper: ToggleableHeapDumper? = null
|
||||
|
||||
override fun setupLeakCanary() {
|
||||
val leakDirectoryProvider = LeakCanaryInternals.getLeakDirectoryProvider(this)
|
||||
val defaultDumper = AndroidHeapDumper(this, leakDirectoryProvider)
|
||||
heapDumper = ToggleableHeapDumper(this, defaultDumper)
|
||||
LeakCanary.refWatcher(this)
|
||||
.heapDumper(heapDumper)
|
||||
.buildAndInstall()
|
||||
}
|
||||
|
||||
override fun toggleLeakCanary(newValue: Boolean) {
|
||||
heapDumper?.enabled = newValue
|
||||
}
|
||||
|
||||
internal class ToggleableHeapDumper(
|
||||
private val context: Context,
|
||||
private val defaultDumper: HeapDumper
|
||||
) : HeapDumper {
|
||||
var prefs: SharedPreferences? = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
var enabled = prefs?.getBoolean(context.getPreferenceKey(pref_key_leakcanary), true) ?: true
|
||||
override fun dumpHeap(): File = if (enabled) defaultDumper.dumpHeap() else HeapDumper.RETRY_LATER
|
||||
}
|
||||
}
|
|
@ -4,8 +4,10 @@
|
|||
|
||||
package org.mozilla.fenix
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import com.squareup.leakcanary.LeakCanary
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -19,7 +21,8 @@ import mozilla.components.support.base.log.sink.AndroidLogSink
|
|||
import org.mozilla.fenix.components.Components
|
||||
import java.io.File
|
||||
|
||||
class FenixApplication : Application() {
|
||||
@SuppressLint("Registered")
|
||||
open class FenixApplication : Application() {
|
||||
lateinit var fretboard: Fretboard
|
||||
|
||||
val components by lazy { Components(this) }
|
||||
|
@ -28,11 +31,23 @@ class FenixApplication : Application() {
|
|||
super.onCreate()
|
||||
Log.addSink(AndroidLogSink())
|
||||
|
||||
if (LeakCanary.isInAnalyzerProcess(this)) {
|
||||
return // don't perform extra init in analyzer
|
||||
}
|
||||
setupLeakCanary()
|
||||
setupCrashReporting()
|
||||
setupGlean(this)
|
||||
loadExperiments()
|
||||
}
|
||||
|
||||
protected open fun setupLeakCanary() {
|
||||
// no-op, LeakCanary is disabled by default
|
||||
}
|
||||
|
||||
open fun toggleLeakCanary(newValue: Boolean) {
|
||||
// no-op, LeakCanary is disabled by default
|
||||
}
|
||||
|
||||
private fun setupGlean(context: Context) {
|
||||
Glean.initialize(context)
|
||||
Glean.setUploadEnabled(BuildConfig.TELEMETRY)
|
||||
|
|
|
@ -13,8 +13,11 @@ import androidx.navigation.Navigation
|
|||
import androidx.preference.Preference
|
||||
import androidx.preference.Preference.OnPreferenceClickListener
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import org.mozilla.fenix.BuildConfig
|
||||
import org.mozilla.fenix.FenixApplication
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.R.string.pref_key_about
|
||||
import org.mozilla.fenix.R.string.pref_key_leakcanary
|
||||
import org.mozilla.fenix.R.string.pref_key_make_default_browser
|
||||
import org.mozilla.fenix.ext.getPreferenceKey
|
||||
import org.mozilla.fenix.ext.requireComponents
|
||||
|
@ -39,13 +42,24 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
|||
private fun setupPreferences() {
|
||||
val makeDefaultBrowserKey = context?.getPreferenceKey(pref_key_make_default_browser)
|
||||
val aboutKey = context?.getPreferenceKey(pref_key_about)
|
||||
val leakKey = context?.getPreferenceKey(pref_key_leakcanary)
|
||||
|
||||
val preferenceMakeDefaultBrowser = findPreference<Preference>(makeDefaultBrowserKey)
|
||||
val preferenceAbout = findPreference<Preference>(aboutKey)
|
||||
val preferenceLeakCanary = findPreference<Preference>(leakKey)
|
||||
|
||||
preferenceMakeDefaultBrowser.onPreferenceClickListener =
|
||||
getClickListenerForMakeDefaultBrowser()
|
||||
preferenceAbout.onPreferenceClickListener = getAboutPageListener()
|
||||
|
||||
preferenceLeakCanary.isVisible = BuildConfig.DEBUG
|
||||
if (BuildConfig.DEBUG) {
|
||||
preferenceLeakCanary.onPreferenceChangeListener =
|
||||
Preference.OnPreferenceChangeListener { _, newValue ->
|
||||
(context?.applicationContext as FenixApplication).toggleLeakCanary(newValue as Boolean)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val defaultClickListener = OnPreferenceClickListener { preference ->
|
||||
|
|
|
@ -18,4 +18,5 @@
|
|||
<string name="pref_key_sign_in" translatable="false">pref_key_sign_in</string>
|
||||
<string name="pref_key_private_mode" translatable="false">pref_key_private_mode</string>
|
||||
<string name="pref_key_theme" translatable="false">pref_key_theme</string>
|
||||
<string name="pref_key_leakcanary" translatable="false">pref_key_leakcanary</string>
|
||||
</resources>
|
||||
|
|
|
@ -105,6 +105,8 @@
|
|||
<string name="preferences_language">Language</string>
|
||||
<!-- Preference for data choices -->
|
||||
<string name="preferences_data_choices">Data choices</string>
|
||||
<!-- Preference for developers -->
|
||||
<string name="preference_leakcanary">Leak Canary</string>
|
||||
|
||||
<!-- Library -->
|
||||
<!-- Option in Library to open Sessions page -->
|
||||
|
|
|
@ -64,6 +64,10 @@
|
|||
android:icon="@drawable/ic_data_collection"
|
||||
android:key="@string/pref_key_data_choices"
|
||||
android:title="@string/preferences_data_choices" />
|
||||
<androidx.preference.SwitchPreference
|
||||
android:icon="@drawable/ic_about"
|
||||
android:key="@string/pref_key_leakcanary"
|
||||
android:title="@string/preference_leakcanary" />
|
||||
</androidx.preference.PreferenceCategory>
|
||||
|
||||
<androidx.preference.PreferenceCategory
|
||||
|
|
|
@ -68,8 +68,8 @@ class ActionBusFactory private constructor(val owner: LifecycleOwner) {
|
|||
|
||||
internal val observer = object : LifecycleObserver {
|
||||
|
||||
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
|
||||
fun onDestroy() {
|
||||
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
|
||||
fun onStop() {
|
||||
map.forEach { entry -> entry.value.onComplete() }
|
||||
buses.remove(owner)
|
||||
}
|
||||
|
@ -113,8 +113,7 @@ class ActionBusFactory private constructor(val owner: LifecycleOwner) {
|
|||
}
|
||||
|
||||
/**
|
||||
* getDestroyObservable observes to Lifecycle owner and fires when
|
||||
* lifecycle.currentState == Lifecycle.State.DESTROYED
|
||||
* getDestroyObservable observes to Lifecycle owner and fires during ON_STOP
|
||||
*/
|
||||
fun getDestroyObservable(): Observable<Unit> {
|
||||
return owner.createDestroyObservable()
|
||||
|
@ -141,8 +140,6 @@ inline fun <reified T : Action> LifecycleOwner.getManagedEmitter(): Observer<T>
|
|||
|
||||
/**
|
||||
* This method returns a destroy observable that can be passed to [org.mozilla.fenix.mvi.UIView]s as needed.
|
||||
* This is deliberately scoped to the attached [LifecycleOwner]'s [Lifecycle.Event.ON_DESTROY]
|
||||
* because a viewholder can be reused across adapter destroys.
|
||||
*/
|
||||
inline fun LifecycleOwner?.createDestroyObservable(): Observable<Unit> {
|
||||
return Observable.create { emitter ->
|
||||
|
@ -152,7 +149,7 @@ inline fun LifecycleOwner?.createDestroyObservable(): Observable<Unit> {
|
|||
return@create
|
||||
}
|
||||
this.lifecycle.addObserver(object : LifecycleObserver {
|
||||
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
|
||||
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
|
||||
fun emitDestroy() {
|
||||
if (emitter.isDisposed) {
|
||||
emitter.onNext(kotlin.Unit)
|
||||
|
|
|
@ -10,6 +10,7 @@ private object Versions {
|
|||
const val rxKotlin = "2.3.0"
|
||||
const val anko = "0.10.8"
|
||||
const val sentry = "1.7.10"
|
||||
const val leakcanary = "1.6.3"
|
||||
|
||||
const val androidx_appcompat = "1.1.0-alpha02"
|
||||
const val androidx_constraint_layout = "2.0.0-alpha2"
|
||||
|
@ -90,6 +91,8 @@ object Deps {
|
|||
const val mozilla_support_ktx = "org.mozilla.components:support-ktx:${Versions.mozilla_android_components}"
|
||||
|
||||
const val sentry = "io.sentry:sentry-android:${Versions.sentry}"
|
||||
const val leakcanary = "com.squareup.leakcanary:leakcanary-android:${Versions.leakcanary}"
|
||||
const val leakcanary_noop = "com.squareup.leakcanary:leakcanary-android-no-op:${Versions.leakcanary}"
|
||||
|
||||
const val junit = "junit:junit:${Versions.junit}"
|
||||
const val tools_test_runner = "com.android.support.test:runner:${Versions.test_tools}"
|
||||
|
|
Loading…
Reference in New Issue