如何解决Android canvas.draw泄漏图形内存
我有一个自定义视图,该视图覆盖了onDraw,并且我注意到图形内存一直在增加超时,直到我的应用因OOM而崩溃(根据设备,它的范围从4h到12h)。
我正在做一些复杂的绘图,但是出于复制目的,此代码可以解决问题:
package com.example.testdrawing;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Nullable;
import java.util.Random;
public class CustomView extends View {
private Random rand;
private Paint paint;
public CustomView(Context context) {
super(context);
init(context);
}
public CustomView(Context context,@Nullable AttributeSet attrs) {
super(context,attrs);
init(context);
}
public CustomView(Context context,@Nullable AttributeSet attrs,int defStyleAttr) {
super(context,attrs,defStyleAttr);
init(context);
}
public CustomView(Context context,int defStyleAttr,int defStyleRes) {
super(context,defStyleAttr,defStyleRes);
init(context);
}
private void init(Context context) {
rand = new Random();
paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(10);
// Simulate invalidation loop
final Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
// I invoke postInvalidate() when the rendering data change.
postInvalidate();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
thread.start();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 1) This one leaks memory
canvas.drawOval(0,500 + (rand.nextInt(100)),900 + (rand.nextInt(100)),paint);
// 2) This one keeps graphic memory at constant
//canvas.drawOval(0,500,900,paint);
}
}
基本上,只要绘图位置是动态的,便会保留内存。如果位置是静态的,则内存保持不变。在这两种情况下,图形内存都不会减少。这是CustomView在约12分钟后的分析器输出:
完整样本here
EDIT(@PerracoLabs):我不认为Random是罪魁祸首。仅通过绘制到动态坐标即可重现。即:
canvas.drawOval(x++ % 500,y++ % 500,w++ % 1080,h++ % 1000,paint);
此外,如果这些只是分配统计信息,为什么还要将它们计入总内存?如果不发布,那就是泄漏,对吗?
奇怪的是,不管画什么,内存增加率都是〜100kb。
编辑2:
我已经附上了生成此示例的完整示例app(在Android 10的Pixel 4上):
由于事件探查器的速度降低到无法使用的程度,因此我已停止进行探查。 请注意,偶尔会在确实释放一些内存的位置掉线。
同样,对我来说,对于几次绘图调用而言,开销大约是分配的200MB图形内存。 我真的很想了解这里发生了什么。显然,在画布上动态位置上进行绘制时与固定位置时相比有所不同,这时内存消耗得以稳定。
解决方法
在测试了代码之后,似乎没有分配内存泄漏,而是分配了尚未回收的内存空间。
在Android中,总的已用内存是包括未使用资源在内的所有内容的总和,不一定是您自己直接分配的资源,可以通过其他方法分配这些资源。
Android使用垃圾收集器来管理内存。垃圾收集器的目标是确保在需要时有足够的可用内存,以最小的CPU开销进行回收,而不是一次性释放尽可能多的内存。
在测试中,draw方法被称为不间断(nonstop),它会继续分配内存,但在达到阈值后,它将不再分配内存。 在这样的测试中,它永远不会停止调用draw方法,这是一个泄漏,内存应始终保持增长而不会停止在任何阈值上,最终系统将终止该应用程序。
接下来是37分钟的屏幕截图。我进行了一些捕获,并在分配信息面板上进行了重叠以更好地理解。如您所见,内存增长是本机的,但是20分钟后,内存增长不再保持在60Mb左右。
请注意,显式调用垃圾回收器不会释放(回收)此类内存,因为System.gc()仅会触发建议,并且由系统决定是否要收集资源,这基本上是在JVM需要内存时。
,我知道这是一个旧帖子,但我最近遇到了同样的问题。
问题出在油漆对象上。我还没有用所有 draw
函数对其进行测试,但是当您使用带有样式 paint
的 STROKE
对象并使用 canvas.drawCircle(x,y,radius,paint)
其中 x
和y
随机更改 - 图形内存将增加,直到不可避免的 OOM 才会释放。这会在 Android 11 上发生,但不会在 Android 7 上发生 - 尚未测试其他版本。
您可以通过像这样创建自定义视图来轻松重现它:
class OOMLayout: FrameLayout {
val paint = Paint().apply {
color = Color.RED
style = Paint.Style.STROKE
strokeWidth = 4f
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val x = (100..400).random().toFloat()
val y = (100..400).random().toFloat()
canvas.drawCircle(x,10f,paint)
invalidate()
return
}
}
这将需要一些时间才能到达 OOM,但在 android 分析器中可以清楚地看到内存泄漏。
为了加快内存消耗,将 drawCircle
调用放入循环 f.e.每个 onDraw
迭代 1000 次 - 这样应用将在几秒钟内 OOM。
使用样式为 FILL
的绘画时 - 在 Android 11 上没有此类问题。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。