我一直在努力识别内存泄漏。我想我的项目circular progress view中有几个内存泄漏。
我的一个猜测是内部类FadeRunnable
中有内存泄漏。
但说实话,我不确切知道如何确定这是否是问题的根源。好吧,当我按常规方案切换方向时,我看到内存使用量增加,如下所示。如果我注释掉FadeRunnable
类的用法,步骤会更小(但仍然存在,所以我猜这不是唯一的泄漏)
一旦我分析堆转储,我就会看到一些东西。但实际上我不知道这些值是什么意思。我做的事情是
这是对的吗?如果是这样,我怎样才能找到保存/保存此对象的转储信息(我想在下方窗格的某处)。
我希望逐步解释如何找出是否以及哪个对象泄漏了一些记忆。
怀疑视图的所有代码都可以在本课程中找到。
但是也可以随时查看整个项目以获得更深入的洞察力,以及是否想要使用它。
提前致谢!
上面的代码链接显示了内存泄漏内部类的固定代码。以下代码段显示了永远不应该使用的原始内存代码
/**
* Mem-leaking code, for fixed code see repository link
* https://github.com/momentummodules/CircularProgressView/blob/master/circularprogressview/src/main/java/momentum/circularprogressview/CircularProgressView.java
*/
public class CircularProgressView extends View
{
...
private Thread fadeThread = null;
...
...
class FadeRunnable implements Runnable
{
@Override
public void run()
{
...
}
}
...
...
private void startFade(boolean fadeIn)
{
// check existing
if(this.fadeThread != null)
{
// check if fade is already running
switch(this.fadeThread.getState())
{
case TERMINATED:
case NEW:
this.fadeThread = null;
break;
case RUNNABLE:
case BLOCKED:
case TIMED_WAITING:
case WAITING:
return;
}
}
// create new
this.fadeThread = new Thread(new FadeRunnable(fadeIn, this.fadeTime));
this.fadeThread.start();
}
}
答案 0 :(得分:5)
是的,FadeRunnable
课程确实存在内存泄漏。
内部类的每个实例都包含对其外部类的隐式引用,可通过OuterClass.this
运算符访问。在您的项目中,当您执行FadeRunnable
然后通过方向更改触发重新配置时,整个活动和包含在其中的CircularProgressView
将被重新创建,但之前的FadeRunnable
仍然存在(已分配)并且,由于它持有对其外部CircularProgressView
类的隐式引用,视图也继续存在,这就是为什么在几次重新配置之后,你在内存中分配了8个CircularProgressView
个实例,并且变得更糟 - 每个View都会保留对它的上下文的引用,而且这也无法释放,导致内存泄漏。
Runnables,Handlers和类似的对象可以将其封闭的活动,片段,视图等用作标准类或STATIC内部类(静态内部类不包含对其外部类的隐式引用) ),并且不应该保留Context
,View
等引用,而是在WeakReference<>
通过配置更改重新创建Activity
时保留View
,可以通过垃圾收集器销毁和释放app.js
。
This是关于这个主题的非常翔实的文章,我强烈建议阅读它。
答案 1 :(得分:2)
我猜你的方向正确。这FadeRunnable
肯定不酷。即使你有其他内存泄漏,你也应该检查一下。
一般来说,你在视图中应该做的事情是完全不同的,特别是视图已经有了处理时间和动画的工具,而不需要线程。
我会建议你,我认为这是一种更简单,更清晰的方法,可以在视图上制作动画。
然后开始动画:
ValueAnimator animation = ValueAnimator.ofFloat(0, 1);
animation.setDuration(500);
animation.addUpdateListener(animationUpdate);
animation.addListener(animationUpdate);
animation.start();
然后你需要那些听众
// this gets called for every animation update,
// inside this call you update `CircularProgressView.this.fadeAlpha`
private final ValueAnimator.AnimatorUpdateListener animationUpdate = new ValueAnimator.AnimatorUpdateListener() {
@Override public void onAnimationUpdate(ValueAnimator animation) {
// this fraction varies between 0f and 1f
float fraction = animation.getAnimatedFraction();
// ... do your calculation
ViewCompat.postInvalidateOnAnimation(CircularProgressView.this);
}
};
// this is an optional one only if you really need
// in that you get notified when the animation starts and ends
private final Animator.AnimatorListener animationListener = new AnimatorListenerAdapter() {
@Override public void onAnimationStart(Animator animation) {
// anything u need goes here
ViewCompat.postInvalidateOnAnimation(CircularProgressView.this);
}
@Override public void onAnimationEnd(Animator animation) {
// anything u need goes here
ViewCompat.postInvalidateOnAnimation(CircularProgressView.this);
}
};
就是这样。
关于实际内存泄漏分析的主题我建议你从现在开始并永远使用泄漏金丝雀库:https://github.com/square/leakcanary这是一个很好的工具来帮助我们(开发人员)跟踪内存泄漏。
修改强>
为什么这个动画有内存泄漏? 这很简单:
startFade(boolean);
上创建一个新线程和一个新的runnable Context
,此上下文为Activity
。因此,在此序列结束时,您的活动不会被GC垃圾收集,AKA:内存泄漏!