Wątek przeniesiony 2023-05-28 01:31 z Java przez Riddle.

Lagowanie dla canvas.draw* po kilkudziesięciu sekundach

0

Tworzę apkę na androida, która ma wyświetlać za pomocą Canvas wiele różnych parametrów w różny sposób. Wykryłem, że mam w kodzie klasę, w której nic specjalnego się nie dzieje, ale gdy jest umieszczona na layoucie i odpalana na urządzeniu z Androidem lub w emulatorze, to po chwili gradualnie przybiera na wykresie wymaganej mocy baterii i mocy procesora - jak dla mnie to dość dziwne zachowanie... W OnDraw chyba nie mam instancjonowania obiektów poza pierwszym wywołaniem, gdzie ustawiam sobie wysokość i szerokość canvas. Czytałem, że ludzie z podobnym problemem sobie radzą, obchodząc to tworzeniem grafiki na bitmapie i dopiero wtedy aplikując bitmapę na Canvas. W trzech innych częściach to pomogło, ale w ostatniej ni chu chu... Co może być problemem?

0

pokaż kod

0

Oto kod klasy:

public class AttitudeMeter extends View {

    private TextPaint tp;
    private float textHeight;
    private Paint basicPaint;
    private float mStringHeight;
    private float mTextWidth;
    private float mTextHeight;
    private Path path;
    private Bitmap mainBitmap;
    private Paint bitmapPaint;
    private float contentWidth;
    private float contentHeight;

    public AttitudeMeter(Context context) {
        super(context);
        init(null, 0);
    }

    public AttitudeMeter(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs, 0);
    }

    public AttitudeMeter(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(attrs, defStyle);
    }

    private void init(AttributeSet attrs, int defStyle) {
        // Load attributes
        final TypedArray a = getContext().obtainStyledAttributes(
                attrs, R.styleable.AttitudeMeter, defStyle, 0);

        mStringHeight = a.getDimension(
                R.styleable.AttitudeMeter_stringHeight,
                mStringHeight
        );
        a.recycle();

        basicPaint = new Paint();
        path = new Path();
        // Update TextPaint and text measurements from attributes
        invalidateTextPaintAndMeasurements();
        Handler redrawHandler = new Handler();
        redrawHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                drawBitmap();
                redrawHandler.postDelayed(this, 1000 / 60);
            }
        }, 1000 / 60);
    }

    private void invalidateTextPaintAndMeasurements() {
        this.tp = new TextPaint();
        this.tp.setTextAlign(Paint.Align.CENTER);
        this.tp.setAntiAlias(true);
        this.tp.setColor(Color.YELLOW);
    }

    private void drawBitmap() {
        if (contentWidth == 0 || contentHeight == 0) {
            return;
        }

//        if (contentHeight > 0) {
//            return;
//        }

        mainBitmap = Bitmap.createBitmap(Math.round(contentWidth), Math.round(contentHeight), Bitmap.Config.ARGB_8888);

        Canvas canvas = new Canvas(mainBitmap);

        this.textHeight = 12 * getHeight() / (float) 220;
        this.tp.setTextSize(this.textHeight);

        canvas.save();
        canvas.rotate((float) MainThread.getCurrentBank(), Math.round(getWidth() / (float) 2), Math.round(getHeight() / (float) 2));

        int halfWidth = Math.round(getWidth() / (float) 2);
        int halfHeight = Math.round(getHeight() / (float) 2);
        basicPaint.setStrokeWidth((float) 2 * getHeight() / 220);
        basicPaint.setAntiAlias(true);
        basicPaint.setColor(Color.rgb(0, 91, 0));
        basicPaint.setStyle(Paint.Style.FILL);
        canvas.drawRect(-halfWidth, -halfHeight, getWidth() + halfWidth, getHeight() + halfHeight, basicPaint);

        basicPaint.setColor(Color.rgb(0, 0, 91));
        int hor = Math.round(getHeight() / (float) 2)
                + Math.round(-MainThread.getCurrentPitch() * 2 * 1.3f / (float) 180 * getHeight());
        canvas.drawRect(-halfWidth, -halfHeight,
                getWidth() + halfWidth,
                hor, basicPaint);

        basicPaint.setColor(Color.WHITE);
        this.drawBankIndicator(canvas, 4, 0, 2.7f);
        this.drawBankIndicator(canvas, 2, 10, 1.8f);
        this.drawBankIndicator(canvas, 2, 20, 1.8f);
        this.drawBankIndicator(canvas, 3, 30, 2.1f);
        this.drawBankIndicator(canvas, 1, 45, 1.0f);
        this.drawBankIndicator(canvas, 3, 60, 2.1f);
        this.drawBankIndicator(canvas, 5, 90, 2.1f);

        int i;
        int cur = hor;
        boolean even = false;
        float evencheck;
        basicPaint.setColor(Color.WHITE);
        this.tp.setColor(Color.WHITE);
        Paint.Align a = this.tp.getTextAlign();
        this.tp.setTextAlign(Paint.Align.LEFT);
        for (i = 0; i < 20; i += 5) {
//            if (i > 40 & !even) {
//                even = !even;
//                cur += Math.round(10 / (float) 180 * getHeight());
//                continue;
//            }

            basicPaint.setStrokeWidth((float) (even ? 2 : 1) * getHeight() / 220);
            even = !even;
            cur += Math.round(13 / (float) 180 * getHeight());
            evencheck = (float) (even ? 20 : i == 15 ? 7.5 : 12);
            canvas.drawLine(getWidth() / (float) 2 - getWidth() / evencheck, cur,
                    getWidth() / (float) 2 + getWidth() / evencheck, cur, basicPaint);
            this.tp.setTextAlign(Paint.Align.LEFT);
            canvas.drawText(Integer.toString(i + 5), getWidth() / (float) 1.95 + getWidth() / evencheck, cur + this.tp.getTextSize() / 3, this.tp);
            this.tp.setTextAlign(Paint.Align.RIGHT);
            canvas.drawText(Integer.toString(i + 5), getWidth() / (float) 2.05 - getWidth() / evencheck, cur + this.tp.getTextSize() / 3, this.tp);
            this.tp.setTextAlign(Paint.Align.LEFT);
        }

        basicPaint.setColor(Color.WHITE);
        basicPaint.setStrokeWidth((float) 2 * getHeight() / 220);
        canvas.drawLine(getWidth() / (float) 2 - getWidth() / (float) 10, hor,
                getWidth() / (float) 2 + getWidth() / (float) 10, hor, basicPaint);
        this.tp.setTextAlign(Paint.Align.LEFT);
        canvas.drawText(Integer.toString(0), getWidth() / (float) 1.95 + getWidth() / (float) 10, hor + this.tp.getTextSize() / 3, this.tp);
        this.tp.setTextAlign(Paint.Align.RIGHT);
        canvas.drawText(Integer.toString(0), getWidth() / (float) 2.05 - getWidth() / (float) 10, hor + this.tp.getTextSize() / 3, this.tp);
        this.tp.setTextAlign(Paint.Align.LEFT);

        cur = hor;
        even = false;
        for (i = 0; i > -20; i -= 5) {
//            if (i < -40 & !even) {
//                even = !even;
//                cur -= Math.round(10 / (float) 180 * getHeight());
//                continue;
//            }

            basicPaint.setStrokeWidth((float) (even ? 2 : 1) * getHeight() / 220);
            even = !even;
            cur -= Math.round(13 / (float) 180 * getHeight());
            evencheck = (float) (even ? 20 : i == -15 ? 7.5 : 12);
            canvas.drawLine(getWidth() / (float) 2 - getWidth() / evencheck, cur,
                    getWidth() / (float) 2 + getWidth() / evencheck, cur, basicPaint);
            this.tp.setTextAlign(Paint.Align.LEFT);
            canvas.drawText(Integer.toString(Math.abs(i - 5)), getWidth() / (float) 1.95 + getWidth() / evencheck, cur + this.tp.getTextSize() / 3, this.tp);
            this.tp.setTextAlign(Paint.Align.RIGHT);
            canvas.drawText(Integer.toString(Math.abs(i - 5)), getWidth() / (float) 2.05 - getWidth() / evencheck, cur + this.tp.getTextSize() / 3, this.tp);
            this.tp.setTextAlign(Paint.Align.LEFT);
        }

        this.tp.setTextAlign(a);
        canvas.restore();
        this.drawCurrentBankValue(canvas);

        basicPaint.setColor(Color.YELLOW);
        basicPaint.setStrokeWidth((float) 4 * getHeight() / 220);
        basicPaint.setStyle(Paint.Style.STROKE);

        path.moveTo(
                halfWidth,
                Math.round(getHeight() / (float) 2)
        );
        path.lineTo(
                halfWidth + halfWidth / (float) 4,
                Math.round(getHeight() / (float) 2) + halfHeight / (float) 40
        );
        path.lineTo(
                halfWidth,
                Math.round(getHeight() / (float) 2 - halfHeight / (float) 50)
        );
        path.lineTo(
                halfWidth - halfWidth / (float) 4,
                Math.round(getHeight() / (float) 2) + halfHeight / (float) 40
        );
        path.lineTo(
                halfWidth,
                Math.round(getHeight() / (float) 2)
        );
        canvas.drawPath(
                path,
                basicPaint
        );
        basicPaint.setColor(Color.WHITE);
        basicPaint.setStrokeWidth((float) 2 * getHeight() / 220);
        canvas.drawLine(
                getWidth() - halfWidth / (float) 4,
                Math.round(getHeight() / (float) 2),
                getWidth(),
                Math.round(getHeight() / (float) 2),
                basicPaint
        );
        canvas.drawLine(
                0,
                Math.round(getHeight() / (float) 2),
                halfWidth / (float) 4,
                Math.round(getHeight() / (float) 2),
                basicPaint
        );

        invalidate();
    }

    void drawBankIndicator(Canvas canvas, float length, int degree, float strokeWidth) {
        Paint p = new Paint();
        p.setColor(Color.WHITE);
        p.setAntiAlias(true);
        p.setStrokeWidth((float) getHeight() / 220 * strokeWidth);

        canvas.rotate(degree, Math.round(getWidth() / (float) 2), Math.round(getHeight() / (float) 2));
        canvas.drawLine(getWidth() / (float) 2, getHeight() / (float) 16, getWidth() / (float) 2, getHeight() / (float) 16 + getHeight() / (24 + length), p);
        canvas.rotate(-degree*2, Math.round(getWidth() / (float) 2), Math.round(getHeight() / (float) 2));
        canvas.drawLine(getWidth() / (float) 2, getHeight() / (float) 16, getWidth() / (float) 2, getHeight() / (float) 16 + getHeight() / (24 + length), p);
        canvas.rotate(degree, Math.round(getWidth() / (float) 2), Math.round(getHeight() / (float) 2));
    }

    void drawCurrentBankValue(Canvas canvas)
    {
        Paint p = new Paint();
        p.setColor(Color.YELLOW);
        p.setAntiAlias(true);
        p.setStrokeWidth((float) getHeight() / 220 * 3);

        canvas.drawLine(getWidth() / (float) 2, getHeight() / (float) 16, getWidth() / (float) 2, getHeight() / (float) 16 + getHeight() / (float) 24, p);

        p.setStyle(Paint.Style.FILL);
        p.setColor(Color.BLACK);

        Rect r = new Rect(getWidth() / 2 - getWidth() / 16, getHeight() / 15,
                getWidth() / 2 + getWidth() / 16, getHeight() / 13);
        canvas.drawRect(r, p);

        canvas.drawText(Integer.toString(Math.abs(MainThread.getCurrentBank())), getWidth() / (float) 2, getHeight() / (float) 15, tp);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (0 == contentHeight) {
            int paddingLeft = getPaddingLeft();
            int paddingRight = getPaddingRight();

            contentWidth = getWidth() - paddingLeft - paddingRight;
            contentHeight = getHeight();
        }

        if (mainBitmap != null) {
            canvas.drawBitmap(mainBitmap, 0, 0, bitmapPaint);
        }
    }

}
1

Nie znam się na androidzie, więc ślepy strzał ;-)

  1. Framework wywołuje onDraw -> tam robisz canvas.drawBitamp i na końcu tego drawBitmap masz invalidate(), które pewnie triggeruje framework do wywołania onDraw, które woła drawBitmap...
  2. Skoro w onDraw dostajesz canvas, to po co tworzysz osobny canvas w onDraw? Nie możesz wykorzystać tego przekazanego? Z tego co widzę, to Canvas ma prywatny stos (save(), restore())
0
yarel napisał(a):

Nie znam się na androidzie, więc ślepy strzał ;-)

  1. Framework wywołuje onDraw -> tam robisz canvas.drawBitamp i na końcu tego drawBitmap masz invalidate(), które pewnie triggeruje framework do wywołania onDraw, które woła drawBitmap...
  2. Skoro w onDraw dostajesz canvas, to po co tworzysz osobny canvas w onDraw? Nie możesz wykorzystać tego przekazanego? Z tego co widzę, to Canvas ma prywatny stos (save(), restore())

onDraw wywołuje drawBitmap na Canvas z onDraw, a nie drawBitmap z obiektu, którego kod tu jest :) drawBitmap z tego obiektu jest wywoływany 60 razy na sekundę przez redrawHandler i invalidate musi być albo w nim, albo w redrawHandlerze, bo w innym przypadku onDraw na obiekcie nie wykona się więcej niż raz :/
Tworzenie osobnego Canvas potrzebne jest mi ze względu na to, że chcę rysować na bitmapie, a dopiero później bitmapę nałożyć na Canvas z onDraw.

0

Widzę na profilerze, że nie tylko w tej klasie występuje problem. Coś musiałem źle ogarnąć z rysowaniem, bo im dłużej chodzi apka, tym więcej procka zżera...

0

Doszedłem do tego, że problemem może być Code Cache, albo ja źle z tego korzystam. Idzie to jakoś wyłączyć?

0

Dobra, ja wymiękam z tym problemem. Jest ktoś w stanie nakierować mnie na jakiś konkretny tutorial, który mówi, jak używać canvas draw tak, żeby to nie lagowało?

1 użytkowników online, w tym zalogowanych: 0, gości: 1