Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support library fragment watcher #1611

Merged
merged 2 commits into from Nov 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion build.gradle
Expand Up @@ -24,6 +24,7 @@ buildscript {
runner : 'androidx.test:runner:1.1.0',
],
],
android_support: 'com.android.support:support-v4:28.0.0',
junit : 'junit:junit:4.12',
kotlin : [
gradlePlugin: "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}",
Expand All @@ -42,7 +43,7 @@ buildscript {
}
dependencies {
classpath deps.kotlin.gradlePlugin
classpath 'com.android.tools.build:gradle:3.5.0'
classpath 'com.android.tools.build:gradle:3.5.2'
classpath 'net.ltgt.gradle:gradle-errorprone-plugin:0.0.16'
classpath 'com.github.ben-manes:gradle-versions-plugin:0.20.0'
classpath "org.jetbrains.dokka:dokka-gradle-plugin:0.9.18"
Expand Down
1 change: 1 addition & 0 deletions leakcanary-android-core/build.gradle
Expand Up @@ -5,6 +5,7 @@ dependencies {
api project(':shark-android')
api project(':leakcanary-object-watcher-android')
api project(':leakcanary-object-watcher-android-androidx')
api project(':leakcanary-object-watcher-android-support-fragments')

implementation deps.kotlin.stdlib

Expand Down
25 changes: 25 additions & 0 deletions leakcanary-object-watcher-android-support-fragments/build.gradle
@@ -0,0 +1,25 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'

dependencies {
api project(':leakcanary-object-watcher-android')

implementation deps.kotlin.stdlib
// Optional dependency
compileOnly deps.android_support
}

android {
compileSdkVersion versions.compileSdk
defaultConfig {
minSdkVersion versions.minSdk
consumerProguardFiles 'consumer-proguard-rules.pro'
}
lintOptions {
disable 'GoogleAppIndexingWarning'
error 'ObsoleteSdkInt'
check 'Interoperability'
}
}

apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
@@ -0,0 +1,2 @@
# AndroidSupportFragmentDestroyWatcher is loaded via reflection
-keep class leakcanary.internal.AndroidSupportFragmentDestroyWatcher { *; }
@@ -0,0 +1,3 @@
POM_ARTIFACT_ID=leakcanary-object-watcher-android-support-fragments
POM_NAME=LeakCanary Object Watcher for Android extension: Android support library fragments support
POM_PACKAGING=aar
@@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.squareup.leakcanary.fragments.android_support" />
@@ -0,0 +1,58 @@
/*
* Copyright (C) 2019 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package leakcanary.internal

import android.app.Activity
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentActivity
import android.support.v4.app.FragmentManager
import leakcanary.AppWatcher.Config
import leakcanary.ObjectWatcher

internal class AndroidSupportFragmentDestroyWatcher(
private val objectWatcher: ObjectWatcher,
private val configProvider: () -> Config
) : (Activity) -> Unit {

private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {

override fun onFragmentViewDestroyed(
fm: FragmentManager,
fragment: Fragment
) {
val view = fragment.view
if (view != null && configProvider().watchFragmentViews) {
objectWatcher.watch(view)
}
}

override fun onFragmentDestroyed(
fm: FragmentManager,
fragment: Fragment
) {
if (configProvider().watchFragments) {
objectWatcher.watch(fragment)
}
}
}

override fun invoke(activity: Activity) {
if (activity is FragmentActivity) {
val supportFragmentManager = activity.supportFragmentManager
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2018 Square, Inc.
* Copyright (C) 2019 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -33,6 +33,10 @@ internal object FragmentDestroyWatcher {
private const val ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME =
"leakcanary.internal.AndroidXFragmentDestroyWatcher"

private const val ANDROID_SUPPORT_FRAGMENT_CLASS_NAME = "android.support.v4.app.Fragment"
private const val ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME =
"leakcanary.internal.AndroidSupportFragmentDestroyWatcher"

fun install(
application: Application,
objectWatcher: ObjectWatcher,
Expand All @@ -46,15 +50,22 @@ internal object FragmentDestroyWatcher {
)
}

if (classAvailable(ANDROIDX_FRAGMENT_CLASS_NAME) &&
classAvailable(ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME)
) {
val watcherConstructor = Class.forName(ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME)
.getDeclaredConstructor(ObjectWatcher::class.java, Function0::class.java)
@kotlin.Suppress("UNCHECKED_CAST")
fragmentDestroyWatchers.add(
watcherConstructor.newInstance(objectWatcher, configProvider) as (Activity) -> Unit
)
getWatcherIfAvailable(
ANDROIDX_FRAGMENT_CLASS_NAME,
ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
objectWatcher,
configProvider
)?.let {
fragmentDestroyWatchers.add(it)
}

getWatcherIfAvailable(
ANDROID_SUPPORT_FRAGMENT_CLASS_NAME,
ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
objectWatcher,
configProvider
)?.let {
fragmentDestroyWatchers.add(it)
}

if (fragmentDestroyWatchers.size == 0) {
Expand All @@ -73,6 +84,26 @@ internal object FragmentDestroyWatcher {
})
}

private fun getWatcherIfAvailable(
fragmentClassName: String,
watcherClassName: String,
objectWatcher: ObjectWatcher,
configProvider: () -> AppWatcher.Config
): ((Activity) -> Unit)? {

return if (classAvailable(fragmentClassName) &&
classAvailable(watcherClassName)
) {
val watcherConstructor = Class.forName(watcherClassName)
.getDeclaredConstructor(ObjectWatcher::class.java, Function0::class.java)
@Suppress("UNCHECKED_CAST")
watcherConstructor.newInstance(objectWatcher, configProvider) as (Activity) -> Unit

} else {
null
}
}

private fun classAvailable(className: String): Boolean {
return try {
Class.forName(className)
Expand Down
1 change: 1 addition & 0 deletions settings.gradle
Expand Up @@ -6,6 +6,7 @@ include ':leakcanary-android-sample'
include ':leakcanary-object-watcher'
include ':leakcanary-object-watcher-android'
include ':leakcanary-object-watcher-android-androidx'
include ':leakcanary-object-watcher-android-support-fragments'
include ':shark'
include ':shark-android'
include ':shark-cli'
Expand Down