美文网首页
关于Android VideoView导致的内存泄漏的问题

关于Android VideoView导致的内存泄漏的问题

作者: 程序员大耳 | 来源:发表于2021-09-27 10:38 被阅读0次

    泄露信息如下:

    ====================================

        HEAP ANALYSIS RESULT

        ====================================

        1 APPLICATION LEAKS

        References underlined with "~~~" are likely causes.

        Learn more at https://squ.re/leaks.

        Signature: 4f5c3ad956b290a0c61f4567bfd87dd73f3578

        ┬───

        │ GC Root: Global variable in native code

        │

        ├─ android.media.PlayerBase$1 instance

        │    Leaking: UNKNOWN

        │    Anonymous subclass of com.android.internal.app.IAppOpsCallback$Stub

        │    ↓ PlayerBase$1.this$0

        │                  ~~~~~~

        ├─ android.media.MediaPlayer instance

        │    Leaking: UNKNOWN

        │    ↓ MediaPlayer.mSubtitleController

        │                  ~~~~~~~~~~~~~~~~~~~

        ├─ android.media.SubtitleController instance

        │    Leaking: UNKNOWN

        │    ↓ SubtitleController.mAnchor

        │                        ~~~~~~~

        ├─ android.widget.VideoView instance

        │    Leaking: YES (View.mContext references a destroyed activity)

        │    View is part of a window view hierarchy

        │    View.mAttachInfo is null (view detached)

        │    View.mID = R.id.video_view

        │    View.mWindowAttachCount = 1

        │    mContext instance of LoginActivity with mDestroyed = true

        │    ↓ View.mContext

        ╰→ LoginActivity instance

            Leaking: YES (ObjectWatcher was watching this because LoginActivity received

            Activity#onDestroy() callback and Activity#mDestroyed is true)

            key = 17268c98-e72a-4afb-9e79-ea4a86bba904

            watchDurationMillis = 5170

            retainedDurationMillis = 166

            mApplication instance of ChatApplication

            mBase instance of androidx.appcompat.view.ContextThemeWrapper

        ====================================

    原因分析:

    1、引用链结构:MediaPlayer 的SubtitleController 引用了VideoView 中的context对象,而VideoView 对象中的mContext引用到了LoginActivity 中context,导致LoginActivity 无法销毁。

    查看源码得知在VideoView 中有如下代码:

    final Context context = getContext();

    final SubtitleController controller =new SubtitleController(context, mMediaPlayer.getMediaTimeProvider(), mMediaPlayer);

    mMediaPlayer.setSubtitleAnchor(controller, this);

    SubtitleController 持有了VideoView 中的context对象,进而MediaPlayer 持有了控件对象,当LoginActivity 无法销毁时却没有释放引用。

    泄漏点的root引用是PlayerBase$1.this$0(PlayeBase的子类是MediaPlayer),这是IAppsCallback$Stub的匿名类,在7.0系统可以看到

    http://androidxref.com/7.0.0_r1/xref/frameworks/base/media/java/android/media/PlayerBase.java

    这个匿名内部类是Binder类,长生命周期持有短生命周期VideoPlayerActivity的引用,导致VideoPlayerActivity的泄漏

    再进一步看,发现8.0以上的手机不会出现这个内存泄漏,原来是系统源码已经解决了这个内存泄漏

    http://androidxref.com/8.0.0_r4/xref/frameworks/base/media/java/android/media/PlayerBase.java

    处理方式是初始化mAppOpsCallback时,new 一个静态内部类,并且这个静态类传入的参数是弱引用

    这是8.0开始做的修复

    2、解决方法:因为在源码层面无法修改源码,在引用端切断引用链。

    import android.content.Context;

    import android.os.Build;

    import android.util.AttributeSet;

    import android.view.ViewGroup;

    import android.widget.VideoView;

    /**

    * 关于Android VideoView导致的内存泄漏的问题

    * https://www.jianshu.com/writer#/notebooks/38625762/notes/93298920

    */

    public class NoMemoryLeakVideoViewextends VideoView {

    public NoMemoryLeakVideoView(Context context) {

    super(context.getApplicationContext());

        }

    public NoMemoryLeakVideoView(Context context, AttributeSet attrs) {

    super(context.getApplicationContext(), attrs);

        }

    public NoMemoryLeakVideoView(Context context, AttributeSet attrs, int defStyleAttr) {

    super(context.getApplicationContext(), attrs, defStyleAttr);

        }

    public void clearMemoryLeak(ViewGroup container) {

    suspend();

            clearFocus();

            // VideoView在发生播放错误的时候,会有弹窗错误提示的Dialog,Dialog依赖传入mContext,如果是Application,那么会报崩溃。

            // 解决方法: 通过查看源码发现,在弹这个dialog的时候,会有条件判断,拦截这个条件不弹错误提示的Dialog即不会崩溃,然后这个Dialog在外部回调接口弹出。这个拦截条件是VideoView的setOnErrorListener的实现方法返回true。

            setOnErrorListener((mp, what, extra) ->true);

            setOnPreparedListener(null);

            setOnCompletionListener(null);

            setOnTouchListener(null);

            setOnClickListener(null);

            setOnDragListener(null);

            setOnKeyListener(null);

            setOnLongClickListener(null);

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {

    setOnApplyWindowInsetsListener(null);

            }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

    setOnScrollChangeListener(null);

            }

    setOnFocusChangeListener(null);

            if (container !=null) {

    container.removeView(this);

            }

    }

    }

    最后在页面销毁的地方调用clearMemoryLeak方法

    相关文章

      网友评论

          本文标题:关于Android VideoView导致的内存泄漏的问题

          本文链接:https://www.haomeiwen.com/subject/tcrcnltx.html