1
0
Fork 0

Fixes #401: Resolve known leak and install LeakCanary

master
Colin Lee 2019-02-13 09:08:35 -06:00 committed by Jeff Boek
parent b4d1ac0d63
commit 2bc769190e
10 changed files with 99 additions and 8 deletions

View File

@ -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

View File

@ -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>

View File

@ -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
}
}

View File

@ -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)

View File

@ -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 ->

View File

@ -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>

View File

@ -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 -->

View File

@ -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

View File

@ -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)

View File

@ -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}"