A13X A13X - 4 months ago 18x
Android Question

SurfaceView Vertical Line Drawing too Slowly across Screen

I have been experimenting with squeezing as much performance out of SurfaceView as possible. Currently, I'm subclassing it and implementing a runnable interface on it instead of a callback. I understand there is no hardware acceleration on it.

Still, if I either draw a canvas primitive vertical line scrolling across the screen or a bitmap vertical line, both run slower and slower after each pass. This felt to me like a memory leak, or is it just Android itself? Is OpenGL or another library really my last resort?

I've drawn plenty of scrolling backgrounds before at decent speeds (I think around 5 pixels per tick, this I'm aiming around 20-50 pixels a tick which if anything would be less stops along the way to render).

EDIT: Here is the SurfaceView extended, the thread it makes, the drawing method, and the initialization of it. Basically, this is in a slightly bigger class that just holds this screen's data. The

methods simply use the canvas primitives or a bitmap to paint mainly as the background, which is a solid background color with some vertical and horizontal lines on it like a music staff, little calculating is involved.

is what makes the scrolling vertical line and when I just let it loop the scrolling from left to right, it eventually lags much slower than the first scroll.

public class MySurfaceView extends SurfaceView implements Runnable
Thread renderThread = null;
SurfaceHolder holder;
volatile boolean running = false;

public MySurfaceView() {
this.holder = getHolder();
holder.setFixedSize(screenW, screenH);

public void resume() {
running = true;
renderThread = new Thread(this);

public void run() {
while (running) {
if (!holder.getSurface().isValid()) {

Canvas canvas = holder.lockCanvas();
if(canvas != null) {

public void pause() {
running = false;
while (true) {
try {
} catch (InterruptedException e) {
// retry


protected void doDraw(Canvas canvas)
canvas.drawColor(Color.rgb(56, 56, 62));

lastNotePlayed = OptionsContainer.getNotePlaying();

//Draw contours (rows).
paint.setColor(Color.rgb(0, 255, 255));
drawContours(canvas, paint);

//Beats per measure (BPM).
paint.setColor(Color.rgb(233, 232, 232));
drawBPM(canvas, paint);

//Draw measures.
drawMeasures(canvas, paint);

//Draw note node inputs.
paint.setColor(Color.rgb(76, 255, 0));
for (int i = 0; i < OptionsContainer.noteList.length; i++) {
if (OptionsContainer.noteList[i].getContour() != 0) {
if (OptionsContainer.noteList[i].getContour() > (OptionsContainer.contour / 2)) {
//Staff on left side, below note.
canvas.drawBitmap(lowerStaffBmp, OptionsContainer.noteList[i].getX(), OptionsContainer.noteList[i].getY(), null);
} else {
canvas.drawBitmap(higherStaffBmp, OptionsContainer.noteList[i].getX(), OptionsContainer.noteList[i].getY() - 40, null);

//Draw cursor.
drawCursor(canvas, paint);

if (OptionsContainer.isRest)
canvas.drawBitmap(restBmp, (OptionsContainer.screenWidth / 2), (screenHeight - 100) / 2, null);

public void init() {
surfaceView = new MySurfaceView();

surfaceView.setOnTouchListener(new View.OnTouchListener() {

public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
// Normalize x,y between 0 and 1
float x = event.getX();
float y = event.getY();

if (x < (OptionsContainer.screenWidth) && y < screenH) {
NoteNode note = new NoteNode(x, y, MainActivity.options);

if (note.getContour() == OptionsContainer.noteList[note.getBeat() - 1].getContour()) {
OptionsContainer.noteList[note.getBeat() - 1] = new NoteNode(x, screenHeight + 200, MainActivity.options);
} else {
OptionsContainer.noteList[note.getBeat() - 1] = note;

return true;

mainActivity.addContentView(surfaceView, layoutParams);

EDIT #2: Final Answer

after the path is drawn in
. I'd imagine that stops a memory leak of that path which is trying to keep track of ALL the paths it has been writing and overwriting, little to our knowledge just looking at the lines on the screen. There was a similar Stack Overflow question but fadden's debugging tips below were very helpful for initially trying to figure out what and where it was going wrong.


"Squeezing performance" and Canvas-rendering don't really go together on a SurfaceView, but you can do okay on many devices.

Grafika's "multi-surface test" Activity features a bouncing circle, rendered in software. I haven't noticed it get slower over time, so I suspect something is wrong in your code. Note Grafika does not subclass SurfaceView, and I generally recommend against doing so -- it's too easy to do the wrong thing. The only valid reason to subclass SurfaceView is if you want to draw on both the Surface and the View, e.g. for some sort of mask effect.

You didn't show any code, so there's not much more we can tell you.

I don't see anything blatantly wrong in the code; seems pretty straightforward. I'd check to make sure OptionsContainer.noteList.length isn't growing without bound. Next step would be to use traceview to figure out which part of the rendering is slow, or just spread System.nanoTime() calls around to identify which part is getting progressively slower. If everything in the method shown is executing at a consistent speed except drawCursor(), move the time-check calls into there, narrowing it down until you find what's draining your performance.

If something is consuming memory quickly enough to cause heap issues, you should see a great deal of GC activity in the logcat output. The DDMS allocation tracker tool can help with that.