Low FPS with SurfaceView on Android 5 - android

I'm doing a pretty standard drawing using SurfaceView.
The code is running smoothly ~60fps on most devices. BUT -
On Android 5, I'm getting real bad performance.
Any clue why?
class MySurfaceView extends SurfaceView implements Runnable{
Thread thread = null;
SurfaceHolder surfaceHolder;
volatile boolean running = false;
public MySurfaceView(Context context) {
super(context);
surfaceHolder = getHolder();
}
public void onResumeMySurfaceView(){
running = true;
thread = new Thread(this);
thread.start();
}
public void onPauseMySurfaceView(){
boolean retry = true;
running = false;
while(retry){
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#Override
public void run() {
while(running){
if(surfaceHolder.getSurface().isValid()){
Canvas canvas = surfaceHolder.lockCanvas();
// drawing
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}

Related

SurfaceView and Game thread synchronization

I have a SurfaceView and a GameThread which updates game state and draw a bitmap on canvas. surface.update() below updates game variables outside the lock and creates a bitmap that has to be drawn on canvas. After locking surface.doDraw draws that bitmap on canvas. Update() is outside UI thread locking is to improve performance. Is this approach wrong? Do I have to update under the lock?
public class GameThread extends Thread {
private SurfaceHolder surfaceHolder;
private GameViewSurface surface;
private boolean running = false;
public GameThread(SurfaceHolder holder, GameViewSurface GameSurface) {
surfaceHolder = holder;
surface = GameSurface;
}
public void setRunning(boolean run) {
running = run;
}
public boolean getRunning() {
return running;
}
#Override
public void run() {
while (running) {
Canvas canvas=null;
try {
if(!surfaceHolder.getSurface().isValid())
continue;
surface.update();
canvas = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
surface.doDraw(canvas);
}
} catch(Exception p){
}finally {
if (canvas != null) {
try {
surfaceHolder.unlockCanvasAndPost(canvas);
}catch (Exception e){}
}
}
}
}
}

View as background on canvas using SurfaceView

How to use View, created in another class, as background(non bitmap) for Canvas on SurfaceView?For example, I made Matrix in 1 class and would like to use it as background in another class which extends SurfaceView?I don't want to add whole Matrix to thread, as I don't want my canvas to repaint it every time thread updates.
Thread t=null;
SurfaceHolder holder;
boolean isRunning=false;
int x=200;
public ZmijicaSV(Context context) {
super(context);
holder=getHolder();
}
public void run()
{
Paint p=new Paint();
p.setColor(Color.BLUE);
p.setStyle(Paint.Style.FILL);
while(isRunning)
{
if(!holder.getSurface().isValid()) {
continue;
}
Canvas c=holder.lockCanvas();
c.drawColor(Color.WHITE);
c.drawRect(x,0,x+100,100,p);
holder.unlockCanvasAndPost(c);
x+=10;
try {
t.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void resume()
{
isRunning=true;
t=new Thread(this);
t.start();
}
public void pause()
{
isRunning=false;
while(true)
{
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
}
t=null;
}

Drawtext on surfaceview does only work on emulator but not on device

I use canvas.drawText on a SurfaceView and the output looks correct on the emulator but when I deploy the app to my device (a Samsung Galaxy S3) the text is written from top to bottom like this:
T
E
s
t
It looks like a line-break is added after each character of the text.
It doesn't matter if the device is landscape or not, it just never works and I can't figure out why.
What am I doing wrong?
In AndroidManifest.xml I use:
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="8" />
The code I'm using:
public class MainActivity extends Activity {
DemoView renderView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
renderView = new DemoView(this);
setContentView(renderView);
}
#Override
public void onResume() {
super.onResume();
renderView.resume();
}
#Override
public void onPause() {
super.onPause();
renderView.pause();
}
private class DemoView extends SurfaceView implements Runnable{
Thread renderThread = null;
SurfaceHolder holder;
volatile boolean running = false;
public DemoView(Context context){
super(context);
this.holder = getHolder();
}
public void resume() {
running = true;
renderThread = new Thread(this);
renderThread.start();
}
public void run() {
Canvas canvas;
while(running) {
if(!holder.getSurface().isValid())
continue;
Paint test = new Paint(Color.YELLOW);
test.setColor(Color.YELLOW);
canvas = holder.lockCanvas();
canvas.drawText("TEst", 10, 10, test);
holder.unlockCanvasAndPost(canvas);
}
}
public void pause() {
running = false;
while(true) {
try {
renderThread.join();
break;
} catch (InterruptedException e) { // retry
}
}
}
}
}
Thanks for your help!
Stephoid
Your use of SurfaceHolder is pretty weird. I'm thinking the unintended behavior is due to that. This is how I've used SurfaceView in the past:
public void run() {
Canvas canvas;
while(running) {
try {
if(!holder.getSurface().isValid())
continue;
canvas = holder.lockCanvas();
synchronized (surface) {
//Code to draw text/etc
}
} catch (...) {
} finally {
holder.unlockCanvasAndPost(canvas);
}
}
}
Note your missing lockCanvas expression to actually assign canvas and pair with unlockCanvasAndPost
The Problem was that I initialized Paint test wrong!
WRONG:
Paint test = new Paint(Color.YELLOW);
Correct:
Paint test = new Paint();
test.setColor(Color.YELLOW);
A very small thing but had big effect!

OnDraw() is not fired, nothing is drawn in surfaceView - Android

HI! I have a surfaceView inside a horizontal scrollview that I want to fill with images with a onDraw() call. However, nothing is drawn.
I have a class in which the drawing is done from the thread CanvasThread.
public class PanelChart extends SurfaceView implements SurfaceHolder.Callback {
private CanvasThread canvasthread ;
public PanelChart(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
getHolder().addCallback(this);
canvasthread = new CanvasThread(getHolder(), this);
setFocusable(true);
I have tried to change the
`synchronized (_surfaceHolder) {
_panel.postInvalidate();
}`
to
synchronized (_surfaceHolder) {
_panel.postInvalidate();
}
I have also tried to add the call setWillNotDraw(false) without luck:
#Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
canvasthread.setRunning(true);
canvasthread.start();
setWillNotDraw(false);
This seems to be a common issue, but none of the solutions I have come across have worked for me.
Thanks!
postInvalidate will not call onDraw with surfaceView. You need to unlock canvas, draw things and then lock canvas. Here is an example of a thread for surfaceView:
class CanvasThread extends Thread {
private SurfaceHolder surfaceHolder;
private PanelChart panel;
private boolean run = false;
public CanvasThread(SurfaceHolder surfaceHolder, PanelChart panel) {
this.surfaceHolder = surfaceHolder;
this.panel = panel;
}
public void setRunning(boolean run) {
this.run = run;
}
public SurfaceHolder getSurfaceHolder() {
return surfaceHolder;
}
#Override
public void run() {
Canvas c;
while (run) {
c = null;
//limit the frame rate to maximum 60 frames per second (16 miliseconds)
timeNow = System.currentTimeMillis();
timeDelta = timeNow - timePrevFrame;
if ( timeDelta < 16){
try{
Thread.sleep(16 - timeDelta);
}catch(InterruptedException e){
}
}
timePrevFrame = System.currentTimeMillis();
try {
c = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
panel.onDraw(c); //draw canvas
computePhysics(); //calculate next frame
}
} finally {
if (c != null) {
surfaceHolder.unlockCanvasAndPost(c); //show canvas
}
}//try finally
} //while
}//run
}//thread

Understanding of SurfaceView with and without SurfaceHolder.Callback

I'm experimenting with SurfaceView and creating simple animations with it, I don't understand why my animation (changins screen color from black to white) is working here (without using SurfaceHolder.Callback)
public class MySurface extends SurfaceView {
private boolean playing = true;
private int counter = 0;
public MySurface(Context context){
super(context);
new Anim().start();
}
private class Anim extends Thread {
#Override
public void run() {
while (playing) {
try {
sleep(1000);
draw();
counter++;
} catch (Exception e){
e.printStackTrace();
}
}
}
private void draw() {
SurfaceHolder holder = getHolder();
Canvas canvas = holder.lockCanvas();
if (canvas != null) {
if (counter % 2 == 0) {
canvas.drawColor(Color.WHITE);
} else
canvas.drawColor(Color.BLACK);
holder.unlockCanvasAndPost(canvas);
}
}
}
}
But it's not working here, what's the difference? I think it might be caused by calling .run() instead of .start()
public class MySurface extends SurfaceView implements SurfaceHolder.Callback {
Anim anim;
private boolean playing = true;
private int counter = 0;
public MySurface(Context context){
super(context);
}
// all Callbacks are overrided i didn add them to make code easier to read
#Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
anim = new Anim();
anim.run();
}
private class Anim extends Thread {
#Override
public void run() {
while (playing) {
try {
sleep(1000);
draw();
counter++;
} catch (Exception e){
e.printStackTrace();
}
}
}
private void draw() {
SurfaceHolder holder = getHolder();
Canvas canvas = holder.lockCanvas();
if (canvas != null) {
if (counter % 2 == 0) {
canvas.drawColor(Color.WHITE);
} else
canvas.drawColor(Color.BLACK);
holder.unlockCanvasAndPost(canvas);
}
}
}
}
EDIT: I forgot to add getHolder().addCallback(this) that caused the issue.
Could someone still point out the differnces between those two methods and tell which one is better?
EDIT2: anim.start() was also needed

Categories

Resources