官方链接
使用 Memory Profiler 查看 Java 堆和内存分配
工具说明
- android studio 版本3.6.1
模拟内存问题场景
- 建立一个新的工程,把下面有问题的自定义VIew,引入到工程里面,最好是写两个activity,把下面自定义view引入到第二个SecondActivity里面,方便测试。
<com.enjoy.memory.widget.IOSStyleLoadingView3
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center" />
public class IOSStyleLoadingView3 extends View {
private final Context context;
private double radius;
private double insideRadius;
private float northwestXStart;
private float northwestYStart;
private float northXStart;
private float northYStart;
private float notheastXStart;
private float notheastYStart;
private float eastXStart;
private float eastYStart;
private float southeastXStart;
private float southeastYStart;
private float southXStart;
private float southYStart;
private float southwestXStart;
private float southwestYStart;
private float westXStart;
private float westYStart;
private float northwestXEnd;
private float northwestYEnd;
private float northXEnd;
private float northYEnd;
private float notheastXEnd;
private float notheastYEnd;
private float eastXEnd;
private float eastYEnd;
private float southeastXEnd;
private float southeastYEnd;
private float southXEnd;
private float southYEnd;
private float southwestXEnd;
private float southwestYEnd;
private float westXEnd;
private float westYEnd;
private int currentColor = 7;
String color[] = new String[]{
"#a5a5a5",
"#b7b7b7",
"#c0c0c0",
"#c9c9c9",
"#d2d2d2",
"#dbdbdb",
"#e4e4e4",
"#e4e4e4"
};
// int[] colors = new int[8];
public IOSStyleLoadingView3(Context context) {
this(context, null, 0);
}
public IOSStyleLoadingView3(Context context, AttributeSet attrs) {
this(context, null, 0);
}
public IOSStyleLoadingView3(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
radius = UIKits.dip2Px(context, 9);
insideRadius = UIKits.dip2Px(context, 5);
// for (int i = 0; i < color.length; i++) {
// colors[i] = Color.parseColor(color[i]);
// }
// paint.setAntiAlias(true);
// paint.setStrokeWidth(UIKits.dip2Px(context, 2));
// paint.setStyle(Paint.Style.STROKE);
// paint.setStrokeCap(Paint.Cap.ROUND);
}
// Path path = new Path();
// Paint paint = new Paint();
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setStrokeWidth(UIKits.dip2Px(context, 2));
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeCap(Paint.Cap.ROUND);
Path p1=new Path();
paint.setColor(Color.parseColor(color[0]));
p1.moveTo(northwestXStart, northwestYStart);
p1.lineTo(northwestXEnd, northwestYEnd);
canvas.drawPath(p1, paint);
Path p2=new Path();
paint.setColor(Color.parseColor(color[1]));
p2.moveTo(northXStart, northYStart);
p2.lineTo(northXEnd, northYEnd);
canvas.drawPath(p2, paint);
Path p3=new Path();
paint.setColor(Color.parseColor(color[2]));
p3.moveTo(notheastXStart, notheastYStart);
p3.lineTo(notheastXEnd, notheastYEnd);
canvas.drawPath(p3, paint);
Path p4=new Path();
paint.setColor(Color.parseColor(color[3]));
p4.moveTo(eastXStart, eastYStart);
p4.lineTo(eastXEnd, eastYEnd);
canvas.drawPath(p4, paint);
Path p5=new Path();
paint.setColor(Color.parseColor(color[4]));
p5.moveTo(southeastXStart, southeastYStart);
p5.lineTo(southeastXEnd, southeastYEnd);
canvas.drawPath(p5, paint);
Path p6=new Path();
paint.setColor(Color.parseColor(color[5]));
p6.moveTo(southXStart, southYStart);
p6.lineTo(southXEnd, southYEnd);
canvas.drawPath(p6, paint);
Path p7=new Path();
paint.setColor(Color.parseColor(color[6]));
p7.moveTo(southwestXStart, southwestYStart);
p7.lineTo(southwestXEnd, southwestYEnd);
canvas.drawPath(p7, paint);
Path p8=new Path();
paint.setColor(Color.parseColor(color[7]));
p8.moveTo(westXStart, westYStart);
p8.lineTo(westXEnd, westYEnd);
canvas.drawPath(p8, paint);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
double centerX = getMeasuredWidth() / 2;
double centerY = getMeasuredHeight() / 2;
double leg = radius * Math.cos(Math.PI / 4);
double insideLeg = insideRadius * Math.cos(Math.PI / 4);
northwestXStart = (float) (centerX - leg);
northwestYStart = (float) (centerY - leg);
northXStart = (float) centerX;
northYStart = (float) (centerY - radius);
notheastXStart = (float) (centerX + leg);
notheastYStart = (float) (centerY - leg);
eastXStart = (float) (centerX + radius);
eastYStart = (float) centerY;
southeastXStart = (float) (centerX + leg);
southeastYStart = (float) (centerY + leg);
southXStart = (float) centerX;
southYStart = (float) (centerY + radius);
southwestXStart = (float) (centerX - leg);
southwestYStart = (float) (centerY + leg);
westXStart = (float) (centerX - radius);
westYStart = (float) centerY;
northwestXEnd = (float) (centerX - insideLeg);
northwestYEnd = (float) (centerY - insideLeg);
northXEnd = (float) centerX;
northYEnd = (float) (centerY - insideRadius);
notheastXEnd = (float) (centerX + insideLeg);
notheastYEnd = (float) (centerY - insideLeg);
eastXEnd = (float) (centerX + insideRadius);
eastYEnd = (float) centerY;
southeastXEnd = (float) (centerX + insideLeg);
southeastYEnd = (float) (centerY + insideLeg);
southXEnd = (float) centerX;
southYEnd = (float) (centerY + insideRadius);
southwestXEnd = (float) (centerX - insideLeg);
southwestYEnd = (float) (centerY + insideLeg);
westXEnd = (float) (centerX - insideRadius);
westYEnd = (float) centerY;
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
startAnimation();
}
private ValueAnimator valueAnimator;
public void startAnimation() {
valueAnimator = ValueAnimator.ofInt(7, 0);
valueAnimator.setDuration(400);
valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
if ((int) animation.getAnimatedValue() != currentColor) {
String b[] = new String[color.length];//移动后的数组
for (int c = 0, size = color.length - 1; c < size; c++) {
b[c + 1] = color[c];
}
b[0] = color[color.length - 1];
color = b;
invalidate();
currentColor = (int) animation.getAnimatedValue();
}
}
});
valueAnimator.start();
}
}
- 启动工程,打开有自定义view的页面
利用Profiler工具进行内存分析
-
找到profiler按钮的位置,打开
在这里插入图片描述
-
点击➕号,选择你的进程
在这里插入图片描述
-
选择MEMORY,因为我们分析的是内存问题,我们会看到Total的总数是一直在增加的,也就是说不断有新对象产生。
-
点击Record按钮,记录一段时间内程序产生的新对象
image.png
通过分析上图,得知一段时间内增加的对象有Paint,Path,String,进一步点击追查发现,是在自定义view的onDraw方法中频繁的创造了这些对象
问题解决与总结
1.在ondraw方法中不要频繁的new对象,因为动画导致ondraw方法频繁调用,就导致了之前的代码频繁的创建一些对象。
Path path = new Path();
Paint paint = new Paint();
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
path.reset();
paint.setColor(colors[currentColor++%8]);
path.moveTo(northwestXStart, northwestYStart);
path.lineTo(northwestXEnd, northwestYEnd);
canvas.drawPath(path, paint);
path.reset();
paint.setColor(colors[currentColor++%8]);
path.moveTo(northXStart, northYStart);
path.lineTo(northXEnd, northYEnd);
canvas.drawPath(path, paint);
path.reset();
paint.setColor(colors[currentColor++%8]);
path.moveTo(notheastXStart, notheastYStart);
path.lineTo(notheastXEnd, notheastYEnd);
canvas.drawPath(path, paint);
path.reset();
paint.setColor(colors[currentColor++%8]);
path.moveTo(eastXStart, eastYStart);
path.lineTo(eastXEnd, eastYEnd);
canvas.drawPath(path, paint);
path.reset();
paint.setColor(colors[currentColor++%8]);
path.moveTo(southeastXStart, southeastYStart);
path.lineTo(southeastXEnd, southeastYEnd);
canvas.drawPath(path, paint);
path.reset();
paint.setColor(colors[currentColor++%8]);
path.moveTo(southXStart, southYStart);
path.lineTo(southXEnd, southYEnd);
canvas.drawPath(path, paint);
path.reset();
paint.setColor(colors[currentColor++%8]);
path.moveTo(southwestXStart, southwestYStart);
path.lineTo(southwestXEnd, southwestYEnd);
canvas.drawPath(path, paint);
path.reset();
paint.setColor(colors[currentColor++%8]);
path.moveTo(westXStart, westYStart);
path.lineTo(westXEnd, westYEnd);
canvas.drawPath(path, paint);
}
- 平时写代码的过程中,要注意对字符串的“+=”操作和截取操作都会创建出对象,在一些频繁被调用的方法中要避免去创建对象。
- 频繁的创建对象会导致jvm虚拟机进行频繁的GC操作,会导致内存抖动,程序出现卡顿和OOM。














网友评论