SharedElement和自定义EnterTransition导致内存泄漏

Ols*_*aqe 7 android memory-leaks android-memory leakcanary

拥有共享元素动画以及自定义输入动画会导致活动泄漏.

知道可能是什么原因?

09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary? * com.feeln.android.activity.MovieDetailActivity has leaked: 09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary? * GC ROOT android.app.ActivityThread$ApplicationThread.this$0 09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary? * references android.app.ActivityThread.mActivities 09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary? * references android.util.ArrayMap.mArray 09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary? * references array java.lang.Object[].[1] 09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary? * references android.app.ActivityThread$ActivityClientRecord.activity 09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary? * references com.feeln.android.activity.MovieDetailActivity.mActivityTransitionState 09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary? * references android.app.ActivityTransitionState.mEnterTransitionCoordinator 09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary? * references android.app.EnterTransitionCoordinator.mEnterViewsTransition 09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary? * references android.transition.TransitionSet.mParent 09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary? * references android.transition.TransitionSet.mListeners 09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary? * references java.util.ArrayList.array 09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary? * references array java.lang.Object[].[1] 09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary? * references android.transition.TransitionManager$MultiListener$1.val$runningTransitions (anonymous class extends android.transition.Transition$TransitionListenerAdapter) 09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary? * references android.util.ArrayMap.mArray 09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary? * references array java.lang.Object[].[2] 09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary? * references com.android.internal.policy.impl.PhoneWindow$DecorView.mContext 09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary? * leaks com.feeln.android.activity.MovieDetailActivity instance 09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary? [ 09-21 16:19:31.007 28269:31066 D/LeakCanary ] * Reference Key: af2b6234-297e-4bab-96e9-02f1c4bca171 09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary? * Device: LGE google Nexus 5 hammerhead 09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary? * Android Version: 5.1.1 API: 22 LeakCanary: 1.3.1 09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary? * Durations: watch=6785ms, gc=262ms, heap dump=8553ms, analysis=33741ms 09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary? [ 09-21 16:19:31.007 28269:31066 D/LeakCanary ]

要重现,您需要拥有一个大的共享图像动画,还需要一个自定义的EnterAnimation和setEnterSharedElementCallback.所有这些都来自支持库.

以下是我设置EnterTransition的方法:

private SharedElementCallback mCallback = new SharedElementCallback() {
    @Override
    public void onSharedElementStart(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots) {
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
        {
            if(sharedElements.size()>0)
                getWindow().setEnterTransition(makeEnterTransition(getWindow().getEnterTransition(), getSharedElement(sharedElements)));
        }
    }


    private View getSharedElement(List<View> sharedElements)
    {
        for (final View view : sharedElements)
        {
            if (view instanceof ImageView)
            {
                return view;
            }
        }
        return null;
    }
};
Run Code Online (Sandbox Code Playgroud)

Ser*_*nko 16

泄漏的情况在于TransitionManager.sRunningTransitions每个DecorView添加和永不删除的地方.DecorView有链接到他ActivityContext.由于sRunningTransitions是静态字段,它具有永久的引用链Activity,它永远不会被GC收集.

我不知道为什么TransitionManager.sRunningTransitions需求,但如果你删除ActivityDecorView,从它,您的问题将得到解决.按照代码示例,怎么做.在您的活动类中:

@Override
protected void onDestroy() {
    super.onDestroy();
    removeActivityFromTransitionManager(Activity activity);
}

private static void removeActivityFromTransitionManager(Activity activity) {
    if (Build.VERSION.SDK_INT < 21) {
        return;
    }
    Class transitionManagerClass = TransitionManager.class;
    try {
        Field runningTransitionsField = transitionManagerClass.getDeclaredField("sRunningTransitions");
            runningTransitionsField.setAccessible(true);
        //noinspection unchecked
        ThreadLocal<WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>>> runningTransitions
                = (ThreadLocal<WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>>>)
                runningTransitionsField.get(transitionManagerClass);
        if (runningTransitions.get() == null || runningTransitions.get().get() == null) {
            return;
        }
        ArrayMap map = runningTransitions.get().get();
        View decorView = activity.getWindow().getDecorView();
        if (map.containsKey(decorView)) {
            map.remove(decorView);
        }
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢你的回答.现在我收到此错误:尝试在android.transition.TransitionManager上的空对象引用上调用虚方法'boolean java.util.ArrayList.remove(java.lang.Object)'$ MultiListener $ 1.onTransitionEnd (3认同)
  • @ fast3r这是跟踪器上问题的链接.他们说它将在Nougat中修复:https://code.google.com/p/android/issues/detail?id = 170469 (2认同)
  • 该解决方案对我有用,但是当您需要处理方向更改和共享元素事务时,它实际上不起作用。不幸的是,它使应用程序崩溃,并在注释中提到了以上崩溃...。 (2认同)

fah*_*hmy 6

@Delargo的解决方案对我不起作用.但是,我在Android问题跟踪器上偶然发现了这个解决方案,它最终为我工作.

我们的想法是在使用活动转换的活动中使用以下类(适当命名LeakFreeSupportSharedElementCallback,从中子类SharedElementCallback化).只需将整个课程复制到您的项目中即可.

  1. LeakFreeSupportSharedElementCallback

您还需要静态方法createDrawableBitmap(Drawable)以及createViewBitmap(View, Matrix, RectF)以下类.这些是由LeakFreeSupportSharedElementCallback班级使用的.

  1. TransitionUtils

获得LeakFreeSupportSharedElementCallback类设置后,将以下内容添加到使用活动转换框架的活动中:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        setEnterSharedElementCallback(new LeakFreeSupportSharedElementCallback());
        setExitSharedElementCallback(new LeakFreeSupportSharedElementCallback());
}
Run Code Online (Sandbox Code Playgroud)

在过渡动画之后,GC释放了内存.