Android memory leak Custom view

Dmitry Lyalin

I'm trying to make small App. This App have Activity, Custom View Class, and service.

1) Activity ask service for new Data and redraw Custom view 2) Service is listning to Bluetooth device and parse data.

Everything was fine, but I noticed that App is slowing down after 40 mins working.

I made another project remove service and find that it slowing too! So problem is my Customview class, maybe i have memory leaks in service to... but i have problem with drawings 100%.

I found that i have some objects that i'm creating on onDraw() method.. i try to move all thise staff to onSizeChanged() - but get more lags.

And now i need help. I need some example with simple drawings that depends on device width and height (I think my method is wrong - i use proportions of my 'Design' to calculate demetions in px)

By the way i'm using animator which make animations more smooth))

public class Dynamics {

    /**
     * Used to compare floats, if the difference is smaller than this, they are
     * considered equal
     */
    private static final float TOLERANCE = 0.01f;

    /** The position the dynamics should to be at */
    private float targetPosition;

    /** The current position of the dynamics */
    private float position;

    /** The current velocity of the dynamics */
    private float velocity;

    /** The time the last update happened */
    private long lastTime;

    /** The amount of springiness that the dynamics has */
    private float springiness;

    /** The damping that the dynamics has */
    private double damping;

    public Dynamics(float springiness, float dampingRatio) {
        this.springiness = springiness;
        this.damping = dampingRatio * 2 * Math.sqrt(springiness);
    }

    public void setPosition(float position, long now) {
        this.position = position;
        lastTime = now;
    }

    public void setVelocity(float velocity, long now) {
        this.velocity = velocity;
        lastTime = now;
    }

    public void setTargetPosition(float targetPosition, long now) {
        this.targetPosition = targetPosition;
        lastTime = now;
    }

    public void update(long now) {
        float dt = Math.min(now - lastTime, 50) / 1000f;

        float x = position - targetPosition;
        double acceleration = -springiness * x - damping * velocity;

        velocity += acceleration * dt;
        position += velocity * dt;

        lastTime = now;
    }

    public boolean isAtRest() {
        final boolean standingStill = Math.abs(velocity) < TOLERANCE;
        final boolean isAtTarget = (targetPosition - position) < TOLERANCE;
        return standingStill && isAtTarget;
    }

    public float getPosition() {
        return position;
    }

    public float getTargetPos() {
        return targetPosition;
    }

    public float getVelocity() {
        return velocity;
    }

}

In my Custom view i have this to set new data:

public void  SetData(int[] NewData2,float[]newDatapoints)
{


    this.NewData=NewData2;

    long now = AnimationUtils.currentAnimationTimeMillis();
    if (datapoints == null || datapoints.length != newDatapoints.length) {
        datapoints = new Dynamics[newDatapoints.length];
        for (int i = 0; i < newDatapoints.length; i++) {
            datapoints[i] = new Dynamics(70f, 0.50f);
            datapoints[i].setPosition(newDatapoints[i], now);
            datapoints[i].setTargetPosition(newDatapoints[i], now);
        }
        invalidate();
    } else {
        for (int i = 0; i < newDatapoints.length; i++) {
            datapoints[i].setTargetPosition(newDatapoints[i], now);
        }
        removeCallbacks(animator);
        post(animator);
    }
    LastData=NewData;

    //redraw();
}

Thise is "code" of my custom view, after all changes it's look terible, so i cut 90% of it. And i make some test code insted:

        import android.content.Context;
        import android.content.res.AssetManager;
        import android.content.res.TypedArray;
        import android.graphics.Canvas;
        import android.graphics.Color;
        import android.graphics.ComposeShader;
        import android.graphics.LinearGradient;
        import android.graphics.Paint;
        import android.graphics.Paint.Style;
        import android.graphics.Path;
        import android.graphics.PorterDuff;
        import android.graphics.RadialGradient;
        import android.graphics.RectF;
        import android.graphics.Shader;
        import android.graphics.Typeface;
        import android.graphics.drawable.Drawable;
        import android.util.AttributeSet;
        import android.util.DisplayMetrics;
        import android.view.View;
        import android.view.animation.AnimationUtils;

        import java.io.IOException;
        import java.math.RoundingMode;
        import java.text.DecimalFormat;
        import java.util.Random;



public class CustomDisplayView extends View {



    //paint for drawing custom view
    private Paint RectPaint = new Paint();

    //Динамические данные float
    private Dynamics[] datapoints;
    //Динамические статические Int
    private int[] NewData = new int[500];
    //созадем новый объект квадрат
    private RectF rectf= new RectF();

    //Задаем массив динамических цветов
    int[] CurColors= new int[100];
    int[] TargetColors= new int[100];

    public CustomDisplayView(Context context, AttributeSet attrs){
        super(context, attrs);

        //Установка парметров красок
        RectPaint.setAntiAlias(true);



    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        if (w != 0 && h != 0) {
            //create Bitmap here

        }

    }

    /**
     * Override the onDraw method to specify custom view appearance using canvas
     */
       @Override
    protected void onDraw(Canvas canvas) {

        //Get Screen size
        //int viewWidth=this.getMeasuredWidth();
       // int viewHeight = this.getMeasuredHeight();


        //Выводим код цвета

        RectPaint.setColor(0xff000000);
        RectPaint.setTextSize(40);


        canvas.restore();
        canvas.drawText("V: " + datapoints[1].getPosition(), 20, 60, RectPaint);

        //int saveCount = canvas.save();
        for(int a=0;a<1000;a++)
        {
            rectf.set(datapoints[a].getPosition(), datapoints[a + 1].getPosition(), datapoints[a].getPosition() + datapoints[a + 1].getPosition() / 10, datapoints[a + 1].getPosition() + datapoints[a + 1].getPosition() / 10);

            RectPaint.setColor(0x88005020);
            //RectPaint.setColor(CurColor[a]);
            //canvas.rotate(datapoints[1].getPosition(), viewWidth/2, viewHeight/2);
            canvas.drawRoundRect(rectf, 0, 0, RectPaint);
            //canvas.restore();
        }
        //canvas.restoreToCount(saveCount);


/*
        for(int a=0;a<999;a++)
        {
            CurColors[a]=progressiveColor(CurColors[a], TargetColors[a], 2);
            if(CurColors[a]==TargetColors[a])
            {
                TargetColors[a]=randomColor();
            }
        }
*/

        canvas.restore();
    }



    //Рандом колор
    public static int randomColor(){
        Random random = new Random();
        int[] ColorParams= new int[4];
        ColorParams[0]=random.nextInt(235)+20;
        ColorParams[1]=random.nextInt(255);
        ColorParams[2]=random.nextInt(255);
        ColorParams[3]=random.nextInt(255);
        return Color.argb(ColorParams[0], ColorParams[1], ColorParams[2], ColorParams[3]);

    }

    //Интерполяция цвета
    public static int progressiveColor(int CurColor,int TargetColor,int Step){

        //Current color
        int[] ColorParams= new int[4];
        ColorParams[0]=(CurColor >> 24) & 0xFF;
        ColorParams[1]=(CurColor >> 16) & 0xFF;
        ColorParams[2]=(CurColor >> 8) & 0xFF;
        ColorParams[3]=CurColor & 0xFF;



        //TargetColor
        int[] TargetColorParams= new int[4];
        TargetColorParams[0]=(TargetColor >> 24) & 0xFF;
        TargetColorParams[1]=(TargetColor >> 16) & 0xFF;
        TargetColorParams[2]=(TargetColor >> 8) & 0xFF;
        TargetColorParams[3]=TargetColor & 0xFF;

        for(int i=0;i<4;i++)
        {
            if(ColorParams[i]<TargetColorParams[i])
            {
                ColorParams[i]+=Step;
                if(ColorParams[i]>TargetColorParams[i])
                {
                    ColorParams[i]=TargetColorParams[i];
                }
            }
            else if(ColorParams[i]>TargetColorParams[i])
            {
                ColorParams[i]-=Step;
                if(ColorParams[i]<TargetColorParams[i])
                {
                    ColorParams[i]=TargetColorParams[i];
                }
            }
        }

        //int red = r - (int)((float)(r*255)/(float)all);
        //int green = (int)((float)(g*255)/(float)all);
        return Color.argb(ColorParams[0], ColorParams[1], ColorParams[2], ColorParams[3]);
        //return String.format("#%06X", (0xFFFFFF & Color.argb(ColorParams[0], ColorParams[1], ColorParams[2], ColorParams[3])));
        //return " "+opacity+" "+red+" "+green+" "+blue;
    }








    //each custom attribute should have a get and set method
    //this allows updating these properties in Java
    //we call these in the main Activity class



    /**
     * Get the current text label color
     * @return color as an int
     */
    public int getLabelColor(){
        return 1;
    }

    /**
     * Set the label color
     * @param newColor new color as an int
     */
    public void setLabelColor(int newColor){
        //update the instance variable
        //labelCol=newColor;
        //redraw the view
        invalidate();
        requestLayout();
    }



    public void redraw(){
        //redraw the view
        invalidate();
        requestLayout();
    }



    public void  SetData(int[] NewData2,float[]newDatapoints)
    {


        this.NewData=NewData2;

        long now = AnimationUtils.currentAnimationTimeMillis();
        if (datapoints == null || datapoints.length != newDatapoints.length) {
            datapoints = new Dynamics[newDatapoints.length];
            for (int i = 0; i < newDatapoints.length; i++) {
                datapoints[i] = new Dynamics(70f, 0.50f);
                datapoints[i].setPosition(newDatapoints[i], now);
                datapoints[i].setTargetPosition(newDatapoints[i], now);
            }
            invalidate();
        } else {
            for (int i = 0; i < newDatapoints.length; i++) {
                datapoints[i].setTargetPosition(newDatapoints[i], now);
            }
            removeCallbacks(animator);
            post(animator);
        }


        //redraw();
    }

    public int GetAction(float x,float y)
    {
        /*
        if(x>(DicsCenterX-LineHalfSpeedZone) && x<(DicsCenterX+LineHalfSpeedZone) && y>(DicsCenterY-PowerOutRadius) && y<(DicsCenterY-SpeedZoneRadius2))
        {
            // private int SpeedZoneRadius2=0;
            // private int PowerOutRadius=0;
            //Смена режима
            //начинаем смену размеру index / ms
            ChangeVal(0,700);

            return 1;
        }
        else if(x>(CofCantBGDrop*2) && x<(CofCantBGDrop*4) && y>(DicsCenterY-PowerOutRadius) && y<(DicsCenterY-SpeedZoneRadius2))
        {
            return 2;
        }
        else
        {
            return 0;
        }
        //return 0;

        */
        return 1;
    }


    public static String fmt(double d)
    {
        double val =  d/100;
        String result;
        if(val == (long) val)
            result= String.format("%d",(long)d);
        else
            result= String.format("%s",d);

        if(result.length()<2)
        {
            String result2=result;
            result="0"+result2;
        }

        return result;
    }


    private Runnable animator = new Runnable() {
        @Override
        public void run() {
            boolean needNewFrame = false;
            long now = AnimationUtils.currentAnimationTimeMillis();
            for (Dynamics dynamics : datapoints) {
                dynamics.update(now);
                if (!dynamics.isAtRest()) {
                    needNewFrame = true;
                }
            }
            if (needNewFrame) {
                postDelayed(this, 15);
            }
            invalidate();
        }
    };

}

I just want to understand where i need to declare scale values, where i need to calc real dimensions in px.. and et.c. to have no memory leaks..

If i remove color change and incrice number of Rects up to 1000 - i get lags.

All methods o any information how to debug memory leaks - you are wellcome!

user3330628

You have to remove runnable for animation when view is detached.

if (needNewFrame) {
    postDelayed(this, 15);  <--- memory leak
} 

Try like this.

@Override
protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    removeCallback(your runnable);
}

Also refreshing every 15 milliseconds is very heavy.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

Does android View reference passed to outside class create memory leak?

From Dev

Android animations memory leak

From Dev

Is it a memory leak in android

From Dev

memory leak with Android WebView

From Dev

Is it a memory leak in android

From Dev

Android - is this a memory leak?

From Dev

Prevent memory leak in Android

From Dev

Memory leak deleting backbone view

From Dev

Android. Memory leak causing out of memory error. Using View Pager.

From Dev

Android inner classes memory leak and leak by context?

From Dev

Android - Device Memory Leak with Fragments

From Dev

Android Fragment Webview Memory Leak

From Dev

Android runOnUiThread causing memory leak

From Dev

Android camera Bitmap memory leak

From Dev

Android progress bar memory leak

From Dev

`Unknown` (`Other`) memory leak in Android?

From Dev

Android runOnUiThread causing memory leak

From Dev

Getting Memory leak on android fragment

From Dev

Android memory leak on device, not on emulator

From Dev

Android Memory Leak - Anonymous class

From Dev

Avoid memory leak with WeakReference Android

From Dev

How to release memory in android to avoid memory leak

From Dev

Returning MVC partial view to jQuery memory leak

From Dev

View Pager Memory Leak with Bitmaps and Volley

From Dev

Memory leak with SpriteKit in Collection View Cells

From Dev

Memory leak detected in Chrome Custom Tabs

From Dev

Swift Decode Array Custom Class Memory Leak

From Dev

Android memory leak on textview - LeakCanary (Leak can be ignored)

From Dev

Fragment view memory leak despite setting parent view to null in onDestroyView