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){}
}
}
}
}
}
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);
}
}
}
}
public class CannonView extends SurfaceView implements SurfaceHolder.Callback {
CannonThread cannonThread;
private Paint blockerPaint;
public CannonView(Context context, AttributeSet attrs) {
super(context, attrs);
blockerPaint = new Paint();
blockerPaint.setStrokeWidth(10.0f);
getHolder().addCallback(this);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
cannonThread = new CannonThread(holder);
cannonThread.running(true);
cannonThread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
cannonThread.running(false);
while (retry) {
try {
cannonThread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
private class CannonThread extends Thread {
boolean setRunning;
SurfaceHolder surfaceHolder;
public CannonThread(SurfaceHolder holder) {
setRunning = true;
surfaceHolder = holder;
}
public void running(boolean isRunning) {
setRunning = isRunning;
}
#Override
public void run() {
Canvas canvas = null;
while (setRunning) {
try {
canvas = surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
canvas.drawLine(0, 0, 100, 100, blockerPaint);
}
} finally {
if (canvas != null)
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
Although above code is very simple
but it is not drawing anything on my activity ..
Logcat says i am doing too much work on main thread ..62 frame skipped ..
Please help
Well this isn't my style of coding, so I decided that you need to simplify things a bit. You used the android api guides but they suck.
Watch the following videos and you should be fine.
http://www.youtube.com/watch?v=wUmId0rwsBQ&list=SP2F07DBCDCC01493A&index=67
http://www.youtube.com/watch?v=0wy907WZFiA&list=SP2F07DBCDCC01493A
http://www.youtube.com/watch?v=ZMcYbf9Hhe4&list=SP2F07DBCDCC01493A
http://www.youtube.com/watch?v=yowNavIDzzE&list=SP2F07DBCDCC01493A
i am using a surface view to draw interactive piechart. here is my code which will looks like all surface view examples.
class PieChart extends SurfaceView implements SurfaceHolder.Callback {
public PieChart(Context context) {
super(context);
// Log.i("PieChart", "PieChart : constructor");
getHolder().addCallback(this);
}
#Override
public void onDraw(Canvas canvas) {
if (hasData) {
resetColor();
try {
canvas.drawColor(getResources().getColor(R.color.graphbg_color));
graphDraw(canvas);
} catch (ValicException ex) {
}
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
Log.i("PieChart", "surfaceChanged");
}
public int callCount = 0;
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
// Log.i("PieChart", "surfaceCreated");
mChartThread = new ChartThread(getHolder(), this);
mChartThread.setRunning(true);
if (!mChartThread.isAlive()) {
mChartThread.start();
}
Rect mFrame = holder.getSurfaceFrame();
mOvalF = new RectF(0, 0, mFrame.right, mFrame.right);
} catch (Exception e) {
// No error message required
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// Log.i("PieChart", "surfaceDestroyed");
boolean retry = true;
callCount = 0;
mChartThread.setRunning(false);
while (retry) {
try {
mChartThread.join();
retry = false;
} catch (InterruptedException e) {
// No error message required
}
}
}
}
class ChartThread extends Thread {
private SurfaceHolder mSurfaceHolder;
private PieChart mPieChart;
private boolean mRefresh = false;
public ChartThread(SurfaceHolder surfaceHolder, PieChart pieChart) {
// Log.i("ChartThread", "ChartThread");
mSurfaceHolder = surfaceHolder;
mPieChart = pieChart;
}
public void setRunning(boolean Refresh) {
// Log.i("ChartThread", "setRunning : " + Refresh);
mRefresh = Refresh;
}
#Override
public void run() {
Canvas c;
// Log.i("ChartThread", "run : " + mRefresh);
while (mRefresh) {
c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
// c.drawColor(0xFFebf3f5);
synchronized (mSurfaceHolder) {
mPieChart.onDraw(c);
}
} catch (Exception ex) {
} finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
with this i am able to draw pie charts successfully. but here the issue is "before loading pie chart black rectangle is visible for a second which is surfaceview's default back ground". so I want to set background color for surface view to avoid the black rectangle.
The following is the changed code for drawing background color to surface view.
public PieChart(Context context) {
super(context);
// Log.i("PieChart", "PieChart : constructor");
getHolder().addCallback(this);
setBackgroundColor(getResources().getColor(R.color.graphbg_color));
}
#Override
public void onDraw(Canvas canvas) {
if (hasData) {
setBackgroundColor(getResources().getColor(R.color.graphbg_color));
resetColor();
try {
canvas.drawColor(getResources().getColor(R.color.graphbg_color));
graphDraw(canvas);
} catch (ValicException ex) {
}
}
}
with these changes, black rectangle issue is resolved. but piechart is not refreshing properly. can someone help me to resolve any of these two issues.
class PieChart extends SurfaceView implements SurfaceHolder.Callback {
private int backGroundColor = Color.BLACK;
public PieChart(Context context,int backGroundColor) {
super(context);
setBackGroundColor(backGroundColor);
// Log.i("PieChart", "PieChart : constructor");
getHolder().addCallback(this);
}
public void setBackGroundColor(int color){
this.backGroundColor = color;
}
#Override
public void onDraw(Canvas canvas) {
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL_AND_STROKE);
paint.setStrokeWidth(3);
paint.setAntiAlias(true);
paint.setColor(backGroundColor);
canvas.drawRect(0, 0, this.getWidth(), this.getHeight(), paint);
if (hasData) {
resetColor();
try {
canvas.drawColor(getResources().getColor(R.color.graphbg_color));
graphDraw(canvas);
} catch (ValicException ex) {
}
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
Log.i("PieChart", "surfaceChanged");
}
public int callCount = 0;
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
// Log.i("PieChart", "surfaceCreated");
mChartThread = new ChartThread(getHolder(), this);
mChartThread.setRunning(true);
if (!mChartThread.isAlive()) {
mChartThread.start();
}
Rect mFrame = holder.getSurfaceFrame();
mOvalF = new RectF(0, 0, mFrame.right, mFrame.right);
} catch (Exception e) {
// No error message required
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// Log.i("PieChart", "surfaceDestroyed");
boolean retry = true;
callCount = 0;
mChartThread.setRunning(false);
while (retry) {
try {
mChartThread.join();
retry = false;
} catch (InterruptedException e) {
// No error message required
}
}
}
}
class ChartThread extends Thread {
private SurfaceHolder mSurfaceHolder;
private PieChart mPieChart;
private boolean mRefresh = false;
public ChartThread(SurfaceHolder surfaceHolder, PieChart pieChart) {
// Log.i("ChartThread", "ChartThread");
mSurfaceHolder = surfaceHolder;
mPieChart = pieChart;
}
public void setRunning(boolean Refresh) {
// Log.i("ChartThread", "setRunning : " + Refresh);
mRefresh = Refresh;
}
#Override
public void run() {
Canvas c;
// Log.i("ChartThread", "run : " + mRefresh);
while (mRefresh) {
c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
// c.drawColor(0xFFebf3f5);
synchronized (mSurfaceHolder) {
mPieChart.onDraw(c);
}
} catch (Exception ex) {
} finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
and in yout class client you can use the setBackGroundColor(your color) ;)
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