Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/218.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android SharedElement和自定义EnterTransition导致内存泄漏_Android_Memory Leaks_Android Memory_Leakcanary - Fatal编程技术网

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

Android SharedElement和自定义EnterTransition导致内存泄漏,android,memory-leaks,android-memory,leakcanary,Android,Memory Leaks,Android Memory,Leakcanary,具有共享元素动画和自定义enter动画会导致活动泄漏 知道原因是什么吗 09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary﹕ * com.feeln.android.activity.MovieDetailActivity已泄漏: 09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary﹕ * GC根android.app.ActivityThread$A

具有共享元素动画和自定义enter动画会导致活动泄漏

知道原因是什么吗

09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary﹕ * com.feeln.android.activity.MovieDetailActivity已泄漏:
09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary﹕ * GC根android.app.ActivityThread$ApplicationThread.this$0
09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary﹕ * 参考android.app.ActivityThread.mActivities
09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary﹕ * 参考android.util.ArrayMap.mArray
09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary﹕ * 引用数组java.lang.Object[].[1]
09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary﹕ * 参考android.app.ActivityThread$ActivityClientRecord.activity
09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary﹕ * 参考com.feeln.android.activity.MovieDetailActivity.mActivityTransitionState
09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary﹕ * 参考android.app.ActivityTransitionState.mEnterTransitionCoordinator
09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary﹕ * 参考android.app.EnterTransitionCoordinator.mEnterViewsTransition
09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary﹕ * 参考android.transition.transition.mParent
09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary﹕ * 参考android.transition.transition.mListeners
09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary﹕ * 参考java.util.ArrayList.array
09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary﹕ * 引用数组java.lang.Object[].[1]
09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary﹕ * 引用android.transition.TransitionManager$MultiListener$1.val$runningTransitions(匿名类扩展了android.transition.transition$TransitionListenerAdapter)
09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary﹕ * 参考android.util.ArrayMap.mArray
09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary﹕ * 引用数组java.lang.Object[].[2]
09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary﹕ * 参考com.android.internal.policy.impl.PhoneWindow$DecorView.mContext
09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary﹕ * 泄漏com.feeln.android.activity.MovieDetailActivity实例
09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary﹕ [09-21 16:19:31.007 28269:31066 D/金丝雀]
*参考键:af2b6234-297e-4bab-96e9-02f1c4bca171
09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary﹕ * 设备:LGE google Nexus 5 hammerhead
09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary﹕ * 安卓版本:5.1.1API:22金丝雀:1.3.1
09-21 16:19:31.007 28269-31066/com.sample.android D/LeakCanary﹕ * 持续时间:观察=6785ms,gc=262ms,堆转储=8553ms,分析=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;
    }
};
private-SharedElementCallback mCallback=new-SharedElementCallback(){
@凌驾
在SharedElementStart(列出sharedElementNames、列出sharedElements、列出sharedElementSnapshots)上公开作废{
if(Build.VERSION.SDK\u INT>=Build.VERSION\u code.LOLLIPOP)
{
if(sharedElements.size()>0)
getWindow().setEnterTransition(makeEnterTransition(getWindow().getEnterTransition(),getSharedElement(sharedElements)));
}
}
私有视图getSharedElement(列出SharedElement)
{
用于(最终视图:sharedElements)
{
if(图像视图的视图实例)
{
返回视图;
}
}
返回null;
}
};

泄漏的原因在于
TransitionManager.s运行Transitions
,其中每个
DecorView
都会添加而不会删除
DecorView
有链接到他的
活动
上下文
。由于
sRunningTransitions
是静态字段,因此它具有指向
活动
的永久引用链,GC永远不会收集这些引用

我不知道为什么TransitionManager.sRunningTransitions需要,但是如果您从中删除
活动
DecorView
,您的问题就会得到解决。下面的代码就是例子,如何做到这一点。在活动课上:

@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();
    }
}
@覆盖
受保护的空onDestroy(){
super.ondestory();
从TransitionManager(活动)移除活动;
}
来自TransitionManager的私有静态void RemoveActivity(活动){
if(Build.VERSION.SDK_INT<21){
返回;
}
类TransitionManager类=TransitionManager.Class;
试一试{
Field runningTransitionsField=transitionManagerClass.getDeclaredField(“sRunningTransitions”);
runningTransitionsField.setAccessible(true);
//未检查
线程本地运行转换
=(本地线程)
运行TransitionField.get(transitionManagerClass);
if(runningTransitions.get()==null | | runningTransitions.get().get()==null){
返回;
}
ArrayMap=runningTransitions.get().get();
View decorView=activity.getWindow().getDecorView();
if(集装箱地图(decorView)){
移除地图(decorView);
}
}捕获(无此字段例外){
e、 printStackTrace();
}捕获(非法访问例外e){
e、 printStackTrace();
}
}

由@Delargo提供的解决方案没有
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        setEnterSharedElementCallback(new LeakFreeSupportSharedElementCallback());
        setExitSharedElementCallback(new LeakFreeSupportSharedElementCallback());
}
Attempt to invoke virtual method 'boolean java.util.ArrayList.remove(java.lang.Object)' on a null object reference
android.transition.TransitionManager$MultiListener$1.onTransitionEnd (TransitionManager.java:306)
mTransition.addListener(new TransitionListenerAdapter() {
    @Override
    public void onTransitionEnd(Transition transition) {
        ArrayList<Transition> currentTransitions =
                   runningTransitions.get(mSceneRoot); //"mSceneRoot" is basically the DecorView
            currentTransitions.remove(transition); //This line crashes, because "currentTransitions" is null
            transition.removeListener(this);
        }
    });
fun AppCompatActivity.removeActivityFromTransitionManager() {
    if (Build.VERSION.SDK_INT < 21) {
        return;
    }
    val transitionManagerClass: Class<*> = TransitionManager::class.java
    try {
        val runningTransitionsField: Field =
            transitionManagerClass.getDeclaredField("sRunningTransitions")
        runningTransitionsField.isAccessible = true
        @Suppress("UNCHECKED_CAST")
        val runningTransitions: ThreadLocal<WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>>?> =
            runningTransitionsField.get(transitionManagerClass) as ThreadLocal<WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>>?>
        if (runningTransitions.get() == null || runningTransitions.get()?.get() == null) {
            return
        }
        val map: ArrayMap<ViewGroup, ArrayList<Transition>> =
            runningTransitions.get()?.get() as ArrayMap<ViewGroup, ArrayList<Transition>>
        map[window.decorView]?.let { transitionList ->
            transitionList.forEach { transition ->
                //Add a listener to all transitions. The last one to finish will remove the decor view:
                transition.addListener(object : Transition.TransitionListener {
                    override fun onTransitionEnd(transition: Transition) {
                        //When a transition is finished, it gets removed from the transition list
                        // internally right before this callback. Remove the decor view only when
                        // all the transitions related to it are done:
                        if (transitionList.isEmpty()) {
                            map.remove(window.decorView)
                        }
                        transition.removeListener(this)
                    }

                    override fun onTransitionCancel(transition: Transition?) {}
                    override fun onTransitionPause(transition: Transition?) {}
                    override fun onTransitionResume(transition: Transition?) {}
                    override fun onTransitionStart(transition: Transition?) {}
                })
            }
            //If there are no active transitions, just remove the decor view immediately:
            if (transitionList.isEmpty()) {
                map.remove(window.decorView)
            }
        }
    } catch (_: Throwable) {}
}