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

Move Notifications to get triggered from NotificationEventListener #2395

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Expand Up @@ -86,6 +86,14 @@ fun interface EventListener {
showIntent: Intent
) : HeapAnalysisDone<HeapAnalysisFailure>(uniqueId, heapAnalysis, showIntent)
}

class NoRetainedObjectFound(
sahil2441 marked this conversation as resolved.
Show resolved Hide resolved
uniqueId: String,
) : Event(uniqueId)

class HeapDumpReceived(
uniqueId: String,
) : Event(uniqueId)
}

/**
Expand Down
Expand Up @@ -5,24 +5,44 @@ import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.os.Build
import android.os.Handler
import android.os.HandlerThread
import com.squareup.leakcanary.core.R
import leakcanary.EventListener.Event
import leakcanary.EventListener.Event.DumpingHeap
import leakcanary.EventListener.Event.HeapAnalysisDone
import leakcanary.EventListener.Event.HeapAnalysisDone.HeapAnalysisSucceeded
import leakcanary.EventListener.Event.HeapAnalysisProgress
import leakcanary.EventListener.Event.HeapDumpFailed
import leakcanary.EventListener.Event.HeapDump
import leakcanary.EventListener.Event.HeapDumpFailed
import leakcanary.internal.InternalLeakCanary
import leakcanary.internal.NotificationReceiver
import leakcanary.internal.NotificationType.LEAKCANARY_LOW
import leakcanary.internal.NotificationType.LEAKCANARY_MAX
import leakcanary.internal.Notifications
import leakcanary.internal.RetainInstanceEvent

private const val BACKGROUND_THREAD_NAME = "Leakcanary-Notification-Event-listener"
private const val DISMISS_NO_RETAINED_OBJECT_NOTIFICATION_MILLIS = 30_000L

object NotificationEventListener : EventListener {

private val appContext = InternalLeakCanary.application
private val notificationManager =
appContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
private val backgroundHandler: Handler
private val scheduleDismissNoRetainedOnTapNotification = {
dismissNoRetainedOnTapNotification()
}
private val scheduleDismissRetainedCountNotification = {
dismissRetainedCountNotification()
}

init {
val handlerThread = HandlerThread(BACKGROUND_THREAD_NAME)
handlerThread.start()
backgroundHandler = Handler(handlerThread.looper)
sahil2441 marked this conversation as resolved.
Show resolved Hide resolved
}

override fun onEvent(event: Event) {
// TODO Unify Notifications.buildNotification vs Notifications.showNotification
Expand All @@ -32,6 +52,7 @@ object NotificationEventListener : EventListener {
}
when (event) {
is DumpingHeap -> {
dismissRetainedCountNotification()
val dumpingHeap = appContext.getString(R.string.leak_canary_notification_dumping)
val builder = Notification.Builder(appContext)
.setContentTitle(dumpingHeap)
Expand Down Expand Up @@ -72,12 +93,77 @@ object NotificationEventListener : EventListener {
} else {
PendingIntent.FLAG_UPDATE_CURRENT
}
val pendingIntent = PendingIntent.getActivity(appContext, 1, event.showIntent, flags)
showHeapAnalysisResultNotification(contentTitle,pendingIntent)
val pendingIntent = PendingIntent.getActivity(appContext, 1, event.showIntent, flags)
showHeapAnalysisResultNotification(contentTitle, pendingIntent)
}

is Event.HeapDumpReceived -> {
dismissNoRetainedOnTapNotification()
}

is Event.NoRetainedObjectFound -> {
val builder = Notification.Builder(appContext)
.setContentTitle(
appContext.getString(R.string.leak_canary_notification_no_retained_object_title)
)
.setContentText(
appContext.getString(
R.string.leak_canary_notification_no_retained_object_content
)
)
.setAutoCancel(true)
.setContentIntent(
NotificationReceiver.pendingIntent(
appContext,
NotificationReceiver.Action.CANCEL_NOTIFICATION
)
)
val notification =
Notifications.buildNotification(appContext, builder, LEAKCANARY_LOW)
notificationManager.notify(
R.id.leak_canary_notification_no_retained_object_on_tap, notification
)

backgroundHandler.postDelayed(
scheduleDismissNoRetainedOnTapNotification,
DISMISS_NO_RETAINED_OBJECT_NOTIFICATION_MILLIS
)
}
}
}

private fun dismissNoRetainedOnTapNotification() {
backgroundHandler.removeCallbacks(scheduleDismissNoRetainedOnTapNotification)
notificationManager.cancel(R.id.leak_canary_notification_no_retained_object_on_tap)
}

private fun dismissRetainedCountNotification() {
backgroundHandler.removeCallbacks(scheduleDismissRetainedCountNotification)
notificationManager.cancel(R.id.leak_canary_notification_retained_objects)
}

private fun showRetainedCountNotification(
objectCount: Int,
contentText: String
) {
backgroundHandler.removeCallbacks(scheduleDismissRetainedCountNotification)
if (!Notifications.canShowNotification) {
return
}
@Suppress("DEPRECATION")
val builder = Notification.Builder(appContext)
.setContentTitle(
appContext.getString(R.string.leak_canary_notification_retained_title, objectCount)
)
.setContentText(contentText)
.setAutoCancel(true)
.setContentIntent(NotificationReceiver.pendingIntent(appContext, NotificationReceiver.Action.DUMP_HEAP))
val notification =
Notifications.buildNotification(appContext, builder, LEAKCANARY_LOW)
notificationManager.notify(R.id.leak_canary_notification_retained_objects, notification)
}


private fun showHeapAnalysisResultNotification(contentTitle: String, showIntent: PendingIntent) {
val contentText = appContext.getString(R.string.leak_canary_notification_message)
Notifications.showNotification(
Expand Down
Expand Up @@ -10,6 +10,8 @@ import android.os.SystemClock
import com.squareup.leakcanary.core.R
import java.util.UUID
import leakcanary.AppWatcher
import leakcanary.EventListener.Event.HeapDumpReceived
import leakcanary.EventListener.Event.NoRetainedObjectFound
import leakcanary.EventListener.Event.DumpingHeap
import leakcanary.EventListener.Event.HeapDump
import leakcanary.EventListener.Event.HeapDumpFailed
Expand Down Expand Up @@ -53,14 +55,6 @@ internal class HeapDumpTrigger(

private var lastHeapDumpUptimeMillis = 0L

private val scheduleDismissRetainedCountNotification = {
dismissRetainedCountNotification()
}

private val scheduleDismissNoRetainedOnTapNotification = {
dismissNoRetainedOnTapNotification()
}

/**
* When the app becomes invisible, we don't dump the heap immediately. Instead we wait in case
* the app came back to the foreground, but also to wait for new leaks that typically occur on
Expand Down Expand Up @@ -154,7 +148,6 @@ internal class HeapDumpTrigger(
return
}

dismissRetainedCountNotification()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this has been replaced by new behavior in the event listener class, as I'm seeing no direct call to dismissRetainedCountNotification()

val visibility = if (applicationVisible) "visible" else "not visible"
dumpHeap(
retainedReferenceCount = retainedReferenceCount,
Expand Down Expand Up @@ -237,32 +230,14 @@ internal class HeapDumpTrigger(

fun onDumpHeapReceived(forceDump: Boolean) {
backgroundHandler.post {
dismissNoRetainedOnTapNotification()
InternalLeakCanary.sendEvent(HeapDumpReceived(currentEventUniqueId!!))
gcTrigger.runGc()
val retainedReferenceCount = objectWatcher.retainedObjectCount
if (!forceDump && retainedReferenceCount == 0) {
SharkLog.d { "Ignoring user request to dump heap: no retained objects remaining after GC" }
@Suppress("DEPRECATION")
val builder = Notification.Builder(application)
.setContentTitle(
application.getString(R.string.leak_canary_notification_no_retained_object_title)
)
.setContentText(
application.getString(
R.string.leak_canary_notification_no_retained_object_content
)
)
.setAutoCancel(true)
.setContentIntent(NotificationReceiver.pendingIntent(application, CANCEL_NOTIFICATION))
val notification =
Notifications.buildNotification(application, builder, LEAKCANARY_LOW)
notificationManager.notify(
R.id.leak_canary_notification_no_retained_object_on_tap, notification
)
backgroundHandler.postDelayed(
scheduleDismissNoRetainedOnTapNotification,
DISMISS_NO_RETAINED_OBJECT_NOTIFICATION_MILLIS
)
InternalLeakCanary.sendEvent(NoRetainedObjectFound(currentEventUniqueId!!))

lastDisplayedRetainedObjectCount = 0
return@post
}
Expand Down Expand Up @@ -403,20 +378,9 @@ internal class HeapDumpTrigger(
notificationManager.notify(R.id.leak_canary_notification_retained_objects, notification)
sahil2441 marked this conversation as resolved.
Show resolved Hide resolved
}

private fun dismissRetainedCountNotification() {
backgroundHandler.removeCallbacks(scheduleDismissRetainedCountNotification)
notificationManager.cancel(R.id.leak_canary_notification_retained_objects)
}

private fun dismissNoRetainedOnTapNotification() {
backgroundHandler.removeCallbacks(scheduleDismissNoRetainedOnTapNotification)
notificationManager.cancel(R.id.leak_canary_notification_no_retained_object_on_tap)
}

companion object {
internal const val WAIT_AFTER_DUMP_FAILED_MILLIS = 5_000L
private const val WAIT_FOR_OBJECT_THRESHOLD_MILLIS = 2_000L
private const val DISMISS_NO_RETAINED_OBJECT_NOTIFICATION_MILLIS = 30_000L
private const val WAIT_BETWEEN_HEAP_DUMPS_MILLIS = 60_000L
}
}
Expand Up @@ -137,7 +137,10 @@ internal object InternalLeakCanary : (Application) -> Unit, OnObjectRetainedList
val backgroundHandler = Handler(handlerThread.looper)

heapDumpTrigger = HeapDumpTrigger(
application, backgroundHandler, AppWatcher.objectWatcher, gcTrigger,
application,
backgroundHandler,
AppWatcher.objectWatcher,
gcTrigger,
sahil2441 marked this conversation as resolved.
Show resolved Hide resolved
configProvider
)
application.registerVisibilityListener { applicationVisible ->
Expand Down