square/leakcanary

Help me solve this leak

recoverrelax opened this issue ยท 10 comments

Leak Canary related report:

  • GC ROOT android.media.PlayerBase$1.this$0 (anonymous subclass of com.android.internal.app.IAppOpsCallback$Stub)
    03-09 10:34:00.954 25381-26910/com.xxx.debug D/LeakCanary: * references android.media.MediaPlayer.mSubtitleController
    03-09 10:34:00.954 25381-26910/com.xxx.debug D/LeakCanary: * references android.media.SubtitleController.mAnchor
    03-09 10:34:00.954 25381-26910/com.xxx.debug D/LeakCanary: * references android.widget.VideoView.mContext
    03-09 10:34:00.954 25381-26910/com.xxx.debug D/LeakCanary: * leaks com.xxx.ui.login.ActAuthWelcomeActivity instance
    03-09 10:34:00.954 25381-26910/com.xxx.debug D/LeakCanary: * Retaining: 4.2 KB.
    03-09 10:34:00.954 25381-26910/com.xxx.debug D/LeakCanary: * Reference Key: 975dd8f6-b2c0-4085-9767-9fe9d4ff4309
    03-09 10:34:00.954 25381-26910/com.xxx.debug D/LeakCanary: * Device: LGE google Nexus 5X bullhead
    03-09 10:34:00.954 25381-26910/com.xxx.debug D/LeakCanary: * Android Version: 7.1.1 API: 25 LeakCanary: 1.5 00f37f5
    03-09 10:34:00.954 25381-26910/com.xxx.debug D/LeakCanary: * Durations: watch=26248ms, gc=143ms, heap dump=2074ms, analysis=137859ms
    03-09 10:34:00.954 25381-26910/com.xxx.debug D/LeakCanary: * Details:
    03-09 10:34:00.954 25381-26910/com.xxx.debug D/LeakCanary: * Instance of android.media.PlayerBase$1
    03-09 10:34:00.954 25381-26910/com.xxx.debug D/LeakCanary: | static $classOverhead = byte[240]@1889707977 (0x70a2a7c9)
    03-09 10:34:00.954 25381-26910/com.xxx.debug D/LeakCanary: | this$0 = android.media.MediaPlayer@852885024 (0x32d5fe20)
    03-09 10:34:00.954 25381-26910/com.xxx.debug D/LeakCanary: | mDescriptor = java.lang.String@1890965472 (0x70b5d7e0)
    03-09 10:34:00.954 25381-26910/com.xxx.debug D/LeakCanary: | mObject = 0
    03-09 10:34:00.954 25381-26910/com.xxx.debug D/LeakCanary: | mOwner = android.media.PlayerBase$1@852966624 (0x32d73ce0)
    03-09 10:34:00.954 25381-26910/com.xxx.debug D/LeakCanary: | shadow$klass = android.media.PlayerBase$1
    03-09 10:34:00.954 25381-26910/com.xxx.debug D/LeakCanary: | shadow$monitor = 0
    03-09 10:34:00.954 25381-26910/com.xxx.debug D/LeakCanary: * Instance of android.media.MediaPlayer

Releasing VideoView resources like this:

video_view.stopPlayback()
video_view.setOnCompletionListener(null)
video_view.setOnPreparedListener(null)

From what i understand from the leak, its caused by this SubtitleController instance in android VideoView inside openVideo:

mMediaPlayer.setSubtitleAnchor(controller, this);

(this reference never released). SubtitleController instance is keeping a context to the videoView throught setAnchor and is never cleared.

Test Device: Nexus 5X Android 7.0
How can i workaround this and fix the issue? Perhaps with reflection, but i see no way

Encountered the same leak, but so far only on Android 7.1.1.

I'm not familiar with MediaPlayer internals, sorry :( Please see How do I fix a memory leak? and other resources for solving leaks.

For anybody who might end up here with the same issue, I've switched to ExoPlayer instead of the native VideoView, after having this issue on Android 7.+. Takes quite a lot of boilerplate code but it might help you

Not sure since i have no code but if you are using MVP as architecture would e pretty easy or Butterknife for the View. Since we don't have any code to help. These are my solutions:

Unbinder unbinder;

on the init: unbinder = butterknife.bind(this);

then onDestroy() unbinder.unbind();

if you are using MVP you could set Custom Method on Destroy() and call it like:

inside onDestroy() presenter.onDestroy();

And inside the presenter you can do: videoView = null;

To clear the resources.

Hope this helps

I am also seeing this issue on 7.1.2

android.media.MediaPlayer.mSubtitleController leaks an instance of my Activity.

same problem on 7.1.1

please reopen this thread. It's not user-made memory leak, it is a sdk leak. I tried to find an workaround but i couldn't. If any1 found a solution, please let us know

@jrodbx ,
seem like same ,

  • xxx.xxx.xxx.course.detail.directory.DetailDirectoryFragment has leaked:
  • GC ROOT android.media.PlayerBase$1.this$0 (anonymous subclass of com.android.internal.app.IAppOpsCallback$Stub)
  • references android.media.MediaPlayer.mSurfaceHolder
  • references android.view.SurfaceView$4.this$0 (anonymous implementation of android.view.SurfaceHolder)
  • references xxx.xxx.videocomponent.core.VideoSurfaceView.mContext
  • references xxx.xxx.xxx.play.PlayActivity.mPlayDirectoryFragment
  • leaks xxx.xxx.xxx.course.detail.directory.DetailDirectoryFragment instance

and i analyse the '.hprof' with mat like this,
mat -

seem like a sdk leak.

Agreed that it seems like an SDK leak. None of the solutions mentioned on this SO post fix it.

Relevant code in VideoView seems to be here:

            mMediaPlayer = new MediaPlayer();
            // TODO: create SubtitleController in MediaPlayer, but we need
            // a context for the subtitle renderers
            final Context context = getContext();
            final SubtitleController controller = new SubtitleController(
                    context, mMediaPlayer.getMediaTimeProvider(), mMediaPlayer);
            controller.registerRenderer(new WebVttRenderer(context));
            controller.registerRenderer(new TtmlRenderer(context));
            controller.registerRenderer(new Cea708CaptionRenderer(context));
            controller.registerRenderer(new ClosedCaptionRenderer(context));
            mMediaPlayer.setSubtitleAnchor(controller, this);

Despite calling viewView.stopPlayback() and/or viewView.suspend() (each of which release the media player and null its reference), the leak does not disappear.

Maybe it would be possible to inflate the VideoView with a different context?

Edit, just tried that, doesn't work. At least in my case, I'm also providing the VideoView a MediaController via

MediaController mediaController = new MediaController(this);

And that is what is leaking the activity. I tried providing an application context, but it crashed. I guess I could try providing some kind of themed context wrapper that wasn't necessarily an activity...?

Any new input relating this issue?