brianwernick / ExoMedia

An Android ExoPlayer wrapper to simplify Audio and Video implementations
Apache License 2.0
2.14k stars 379 forks source link

LeakCanary reports AudioManager leak #432

Closed stanmots closed 7 years ago

stanmots commented 7 years ago
Include the following:
Reproduction Steps

This is a known issue of the Android's AudioManager. It was added to the LeakCanary excluded list since it was officially fixed in Android M. More info here. However, there is a fix for older API which you can find here.

I've looked through the ExoMedia source code and it seems that you just need to replace this line with

audioManager = (AudioManager) context.getApplicationContext()
                .getSystemService(Context.AUDIO_SERVICE);

i.d. the audio manager should use the application context.

Here is the LeakCanary relevant logcat snippet:

: * EXCLUDED LEAK.
: * com.test.VideoActivity has leaked:
: * GC ROOT android.media.AudioManager$1.this$0 (anonymous subclass of android.media.IAudioFocusDispatcher$Stub) , matching exclusion field android.media.AudioManager$1#this$0
: * references android.media.AudioManager.mContext
: * references android.app.ContextImpl.mOuterContext
: * leaks com.test.VideoActivity instance
: * Retaining: 83KB.
: * Reference Key: f1899298-cc39-44c1-92d3-6d7d2a329421
: * Device: unknown generic_x86 Android SDK built for x86 google_sdk_x86
: * Android Version: 4.4.2 API: 19 LeakCanary: 1.5 00f37f5
: * Durations: watch=6084ms, gc=129ms, heap dump=200ms, analysis=6601ms
: * Details:
: * Instance of android.media.AudioManager$1
: |   this$0 = android.media.AudioManager@2907338536 (0xad4a7728)
: |   mDescriptor = java.lang.String@2900754184 (0xace5ff08)
: |   mOwner = android.media.AudioManager$1@2907338776 (0xad4a7818)
: |   mObject = -1213348400
: * Instance of android.media.AudioManager
: |   static ACTION_SCO_AUDIO_STATE_UPDATED = java.lang.String@2900750592 (0xace5f100)
: |   static sService = android.media.IAudioService$Stub$Proxy@2902817792 (0xad057c00)
: |   static MODE_IN_COMMUNICATION = 3
: |   static MASTER_MUTE_CHANGED_ACTION = java.lang.String@2900752112 (0xace5f6f0)
: |   static EXTRA_PREV_MASTER_VOLUME_VALUE = java.lang.String@2900750984 (0xace5f288)
: |   static DEVICE_OUT_USB_ACCESSORY = 8192
: |   static FLAG_PLAY_SOUND = 4
: |   static $staticOverhead = byte[2616]@2900747561 (0xace5e529)
: |   static DEVICE_OUT_DEFAULT = 1073741824
: |   static DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 512
: |   static AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE = 4
: |   static FX_KEYPRESS_INVALID = 9
: |   static ROUTE_SPEAKER = 2
: |   static STREAM_VOICE_CALL = 0
: |   static VIBRATE_SETTING_CHANGED_ACTION = java.lang.String@2900752776 (0xace5f988)
: |   static STREAM_MUSIC = 3
: |   static PROPERTY_OUTPUT_SAMPLE_RATE = java.lang.String@2900752520 (0xace5f888)
: |   static AUDIOFOCUS_GAIN_TRANSIENT = 2
: |   static MODE_IN_CALL = 2
: |   static ROUTE_EARPIECE = 1
: |   static RINGER_MODE_CHANGED_ACTION = java.lang.String@2900752656 (0xace5f910)
: |   static SCO_AUDIO_STATE_CONNECTING = 2
: |   static TAG = java.lang.String@2900753024 (0xace5fa80)
: |   static STREAM_SYSTEM_ENFORCED = 7
: |   static FLAG_BLUETOOTH_ABS_VOLUME = 64
: |   static ADJUST_LOWER = -1
: |   static FX_FOCUS_NAVIGATION_UP = 1
: |   static STREAM_RING = 2
: |   static ADJUST_SAME = 0
: |   static RINGER_MODE_MAX = 2
: |   static ROUTE_HEADSET = 8
: |   static AUDIOFOCUS_LOSS = -1
: |   static EXTRA_VIBRATE_TYPE = java.lang.String@2900751744 (0xace5f580)
: |   static RINGER_MODE_VIBRATE = 1
: |   static FLAG_ALLOW_RINGER_MODES = 2
: |   static AUDIOFOCUS_GAIN = 1
: |   static VIBRATE_SETTING_ONLY_SILENT = 2
: |   static STREAM_ALARM = 4
: |   static FX_FOCUS_NAVIGATION_DOWN = 2
: |   static EXTRA_MASTER_VOLUME_MUTED = java.lang.String@2900750728 (0xace5f188)
: |   static VIBRATE_SETTING_ON = 1
: |   static AUDIOFOCUS_REQUEST_GRANTED = 1
: |   static STREAM_TTS = 9
: |   static DEVICE_OUT_EARPIECE = 1
: |   static FX_FOCUS_NAVIGATION_LEFT = 3
: |   static EXTRA_SCO_AUDIO_STATE = java.lang.String@2900751504 (0xace5f490)
: |   static RINGER_MODE_NORMAL = 2
: |   static FX_KEYPRESS_SPACEBAR = 6
: |   static DEVICE_OUT_DGTL_DOCK_HEADSET = 4096
: |   static EXTRA_VOLUME_STREAM_VALUE = java.lang.String@2900751984 (0xace5f670)
: |   static EXTRA_PREV_VOLUME_STREAM_VALUE = java.lang.String@2900751120 (0xace5f310)
: |   static EXTRA_MASTER_VOLUME_VALUE = java.lang.String@2900750856 (0xace5f208)
: |   static ROUTE_BLUETOOTH = 4
: |   static DEVICE_OUT_AUX_DIGITAL = 1024
: |   static STREAM_SYSTEM = 1
: |   static DEFAULT_STREAM_VOLUME = int[10]@2900753096 (0xace5fac8)
: |   static FLAG_SHOW_UI = 1
: |   static ADJUST_RAISE = 1
: |   static DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 256
: |   static STREAM_DTMF = 8
: |   static STREAM_NOTIFICATION = 5
: |   static SCO_AUDIO_STATE_DISCONNECTED = 0
: |   static FX_KEYPRESS_DELETE = 7
: |   static FLAG_FIXED_VOLUME = 32
: |   static ACTION_SCO_AUDIO_STATE_CHANGED = java.lang.String@2900750464 (0xace5f080)
: |   static AUDIOFOCUS_LOSS_TRANSIENT = -2
: |   static MODE_RINGTONE = 1
: |   static DEVICE_OUT_WIRED_HEADSET = 4
: |   static VIBRATE_SETTING_OFF = 0
: |   static MODE_NORMAL = 0
: |   static SCO_AUDIO_STATE_ERROR = -1
: |   static RINGER_MODE_SILENT = 0
: |   static AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK = -3
: |   static DEVICE_OUT_BLUETOOTH_A2DP = 128
: |   static DEVICE_OUT_ANLG_DOCK_HEADSET = 2048
: |   static USE_DEFAULT_STREAM_TYPE = -2147483648
: |   static ROUTE_BLUETOOTH_SCO = 4
: |   static VIBRATE_TYPE_NOTIFICATION = 1
: |   static SCO_AUDIO_STATE_CONNECTED = 1
: |   static DEVICE_OUT_BLUETOOTH_SCO = 16
: |   static ACTION_AUDIO_BECOMING_NOISY = java.lang.String@2900750344 (0xace5f008)
: |   static VOLUME_CHANGED_ACTION = java.lang.String@2900752904 (0xace5fa08)
: |   static FX_FOCUS_NAVIGATION_RIGHT = 4
: |   static EXTRA_VIBRATE_SETTING = java.lang.String@2900751624 (0xace5f508)
: |   static FLAG_VIBRATE = 16
: |   static FLAG_REMOVE_SOUND_AND_VIBRATE = 8
: |   static FX_KEYPRESS_STANDARD = 5
: |   static DEVICE_OUT_SPEAKER = 2
: |   static DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 32
: |   static ROUTE_BLUETOOTH_A2DP = 16
: |   static PROPERTY_OUTPUT_FRAMES_PER_BUFFER = java.lang.String@2900752376 (0xace5f7f8)
: |   static STREAM_BLUETOOTH_SCO = 6
: |   static DEVICE_OUT_USB_DEVICE = 16384
: |   static DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 64
: |   static FX_KEY_CLICK = 0
: |   static MASTER_VOLUME_CHANGED_ACTION = java.lang.String@2900752240 (0xace5f770)
: |   static AUDIOFOCUS_REQUEST_FAILED = 0
: |   static NUM_SOUND_EFFECTS = 10
: |   static NUM_STREAMS = 5
: |   static AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK = 3
: |   static FX_KEYPRESS_RETURN = 8
: |   static MODE_CURRENT = -1
: |   static EXTRA_VOLUME_STREAM_TYPE = java.lang.String@2900751856 (0xace5f5f0)
: |   static EXTRA_SCO_AUDIO_PREVIOUS_STATE = java.lang.String@2900751368 (0xace5f408)
: |   static ROUTE_ALL = -1
: |   static MODE_INVALID = -2
: |   static VIBRATE_TYPE_RINGER = 0
: |   static DEVICE_OUT_WIRED_HEADPHONE = 8
: |   static AUDIOFOCUS_NONE = 0
: |   static EXTRA_RINGER_MODE = java.lang.String@2900751256 (0xace5f398)
: |   mAudioFocusDispatcher = android.media.AudioManager$1@2907338776 (0xad4a7818)
: |   mAudioFocusEventHandlerDelegate = android.media.AudioManager$FocusEventHandlerDelegate@2907338712 (0xad4a77d8)
: |   mAudioFocusIdListenerMap = java.util.HashMap@2907338656 (0xad4a77a0)
: |   mContext = android.app.ContextImpl@2909980064 (0xad72c5a0)
: |   mFocusListenerLock = java.lang.Object@2909937776 (0xad722070)
: |   mICallBack = android.os.Binder@2907338848 (0xad4a7860)
: |   mToken = android.os.Binder@2907338592 (0xad4a7760)
: |   mUseMasterVolume = false
: |   mVolumeKeyUpTime = 0
: |   mUseVolumeKeySounds = true
: * Instance of android.app.ContextImpl
: |   static sSharedPrefs = null
: |   static sNextPerContextServiceCacheIndex = 39
: |   static SYSTEM_SERVICE_MAP = java.util.HashMap@2900513928 (0xace25488)
: |   static WALLPAPER_FETCHER = android.app.ContextImpl$1@2900351896 (0xacdfdb98)
: |   static $staticOverhead = byte[168]@2900469497 (0xace1a6f9)
: |   static TAG = java.lang.String@2900513856 (0xace25440)
: |   static EMPTY_FILE_LIST = java.lang.String[0]@2900336392 (0xacdf9f08)
: |   static DEBUG = false
: |   mActivityToken = android.os.BinderProxy@2907165080 (0xad47d198)
: |   mBasePackageName = java.lang.String@2902656888 (0xad030778)
: |   mCacheDir = null
: |   mContentResolver = android.app.ContextImpl$ApplicationContentResolver@2903362440 (0xad0dcb88)
: |   mDatabasesDir = null
: |   mDisplay = null
: |   mDisplayAdjustments = android.view.DisplayAdjustments@2907538064 (0xad4d8290)
: |   mExternalCacheDirs = null
: |   mExternalFilesDirs = null
: |   mExternalObbDirs = null
: |   mFilesDir = null
: |   mMainThread = android.app.ActivityThread@2902654384 (0xad02fdb0)
: |   mOpPackageName = java.lang.String@2902656888 (0xad030778)
: |   mOuterContext = com.test.VideoActivity@2903352256 (0xad0da3c0)
: |   mPackageInfo = android.app.LoadedApk@2902670056 (0xad033ae8)
: |   mPackageManager = android.app.ApplicationPackageManager@2907528496 (0xad4d5d30)
: |   mPreferencesDir = null
: |   mReceiverRestrictedContext = null
: |   mResources = android.content.res.Resources@2902671208 (0xad033f68)
: |   mResourcesManager = android.app.ResourcesManager@2902655248 (0xad030110)
: |   mUser = android.os.UserHandle@2907208904 (0xad487cc8)
: |   mServiceCache = java.util.ArrayList@2907534568 (0xad4d74e8)
: |   mSync = java.lang.Object@2907219520 (0xad48a640)
: |   mTheme = android.content.res.Resources$Theme@2907337608 (0xad4a7388)
: |   mThemeResource = 16974120
: |   mRestricted = false
: * Excluded Refs:
: | Field: android.app.ActivityThread$ActivityClientRecord.nextIdle
: | Field: android.widget.Editor$EasyEditSpanController.this$0
: | Field: android.widget.Editor$SpanController.this$0
: | Field: android.os.Message.obj
: | Field: android.os.Message.next
: | Field: android.os.Message.target
: | Field: android.view.inputmethod.InputMethodManager.mNextServedView
: | Field: android.view.inputmethod.InputMethodManager.mServedView
: | Field: android.view.inputmethod.InputMethodManager.mServedInputConnection
: | Field: android.view.inputmethod.InputMethodManager.mCurRootView
: | Field: android.animation.LayoutTransition$1.val$parent
: | Field: android.view.textservice.SpellCheckerSession$1.this$0
: | Field: android.support.v7.internal.widget.ActivityChooserModel.mActivityChoserModelPolicy
: | Field: android.widget.ActivityChooserModel.mActivityChoserModelPolicy
: | Field: android.speech.SpeechRecognizer$InternalListener.this$0
: | Field: android.accounts.AccountManager$AmsTask$Response.this$1
: | Field: android.media.MediaScannerConnection.mContext
: | Field: android.os.UserManager.mContext
: | Field: android.appwidget.AppWidgetHost$Callbacks.this$0
: | Field: android.media.AudioManager$1.this$0
: | Field: android.widget.Editor$Blink.this$0
: | Field: android.net.ConnectivityManager.sInstance
: | Field: android.view.Choreographer$FrameDisplayEventReceiver.mMessageQueue (always)
: | Static field: android.text.TextLine.sCached
: | Thread:FinalizerWatchdogDaemon (always)
: | Thread:main (always)
: | Thread:LeakCanary-Heap-Dump (always)
: | Class:java.lang.ref.WeakReference (always)
: | Class:java.lang.ref.SoftReference (always)
: | Class:java.lang.ref.PhantomReference (always)
: | Class:java.lang.ref.Finalizer (always)
: | Class:java.lang.ref.FinalizerReference (always)
brianwernick commented 7 years ago

This isn't an issue in ExoMedia itself, nor is the fix something that would be owned by ExoMedia (as it requires Activity modifications). I can make the changes to the demo app though

stanmots commented 7 years ago

Yes, you are right. It's one of the Android's bugs. I just thought it would be better to change the context to the application context to prevent LeakCanary warnings.

Currently I'm using a VideoView subclass where I'm overriding the setup method for this purpose:

    override fun setup(context: Context, attrs: AttributeSet?) {
        if (isInEditMode) {
            return
        }

        audioManager = context.applicationContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager

        val attributeContainer = AttributeContainer(context, attrs)
        initView(context, attributeContainer)
        postInit(attributeContainer)
    }
brianwernick commented 7 years ago

Oh, yeah I can do that; or you can change it in the dev_v4 branch and create a PR

stanmots commented 7 years ago

Ok, I'll do it