Jan 24, 2012

Link SurfaceView and Background Thread work together

In previous posts "Create SurfaceView for our game" and "Implement background thread for our game", we have created DUMMY MyGameSurfaceView and MyGameThread classes, without any function, and without any interaction. In this step, we are going to modify the Java code to make them work together.



Modify main.xml to include MyGameSurfaceView in the layout.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >

<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />
<com.MyGame.MyGameSurfaceView
android:id="@+id/myview1"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>


Modify the main activity (MyGameActivity), call myGameSurfaceView1.MyGameSurfaceView_OnResume() in onResume() and call myGameSurfaceView1.MyGameSurfaceView_OnPause() in onPause(); to start and stop the background thread.
package com.MyGame;

import android.app.Activity;
import android.os.Bundle;

public class MyGameActivity extends Activity {

MyGameSurfaceView myGameSurfaceView1;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
myGameSurfaceView1 = (MyGameSurfaceView)findViewById(R.id.myview1);

}

@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
myGameSurfaceView1.MyGameSurfaceView_OnResume();
}

@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
myGameSurfaceView1.MyGameSurfaceView_OnPause();
}

}


Modify MyGameThread.java. It's a classic structure of backgraound thread. One thing have to note is the statement parent.updateSurfaceView() in run(), it will call back the function updateSurfaceView() in it's parent object.
package com.MyGame;

public class MyGameThread extends Thread {

volatile boolean running = false;

MyGameSurfaceView parent;
long sleepTime;

MyGameThread(MyGameSurfaceView sv, long st){
super();
parent = sv;
sleepTime = st;
}

public void setRunning(boolean r){
running = r;
}

@Override
public void run() {
// TODO Auto-generated method stub
while(running){

try {
sleep(sleepTime);
parent.updateSurfaceView();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}

}


Modify MyGameSurfaceView.java. It's the main part in the code. updateSurfaceView() is a call back function from background thread (MyGameThread.java) in pre-defined duration reached. In-directly, it call updateStates() and onDraw(canvas) to update states (not yet implemented) and draw something on screen (some random drawing is implemented). Please note that updateSurfaceView() is called from background thread, that means it run in background thread, not in UI thread. That's why we need something like surfaceHolder.lockCanvas(), surfaceHolder.unlockCanvasAndPost(canvas) and synchronized (surfaceHolder).
package com.MyGame;

import java.util.Random;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MyGameSurfaceView extends SurfaceView implements SurfaceHolder.Callback{

SurfaceHolder surfaceHolder;

MyGameThread myGameThread = null;

private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
Random random;

public MyGameSurfaceView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}

public MyGameSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}

public MyGameSurfaceView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// TODO Auto-generated method stub

}

@Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub

}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub

}

public void MyGameSurfaceView_OnResume(){

random = new Random();
surfaceHolder = getHolder();
getHolder().addCallback(this);

//Create and start background Thread
myGameThread = new MyGameThread(this, 500);
myGameThread.setRunning(true);
myGameThread.start();

}

public void MyGameSurfaceView_OnPause(){
//Kill the background Thread
boolean retry = true;
myGameThread.setRunning(false);

while(retry){
try {
myGameThread.join();
retry = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

@Override
protected void onDraw(Canvas canvas) {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);

int w = canvas.getWidth();
int h = canvas.getHeight();
int x = random.nextInt(w-1);
int y = random.nextInt(h-1);
int r = random.nextInt(255);
int g = random.nextInt(255);
int b = random.nextInt(255);

paint.setColor(0xff000000 + (r << 16) + (g << 8) + b);
canvas.drawPoint(x, y, paint);
}

public void updateStates(){
//Dummy method() to handle the States
}

public void updateSurfaceView(){
//The function run in background thread, not ui thread.

Canvas canvas = null;

try{
canvas = surfaceHolder.lockCanvas();

synchronized (surfaceHolder) {
updateStates();
onDraw(canvas);
}
}finally{
if(canvas != null){
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}

}


The idea is:
  • The main.xml layout define MyGameSurfaceView.
  • MyGameSurfaceView start background thread.
  • When background thread reach the pre-defined duration, it call back updateSurfaceView() function of
  • MyGameSurfaceView to do and draw something.


Next:
- Flickering problems due to double buffer of SurfaceView

6 comments:

  1. Hi, ive tried everything step by step, the thing is,after the text view (Hello World) nothing is shown on my app screen other than a blank white screen ( i saw error log of xml file it says" java.lang null pointer exception")
    please help (either xml is wrong or my surfaceview class is incorrect :( , i tried a bit of nooby debugging it seems it never enters the surfaceCreated() mehtod)

    ReplyDelete
  2. Greetings - nice tutorials ... still usable even with Android 5 on the horizon ! :))

    @NoobRon - nearly a year later :)) ...

    Trying this tutorial, I came across the same problems, fixed them, and tested before making this post. The problems are interrelated, hard to find (because they are HeisenBugs :)) , but easy to fix !

    The original code (created in the previous section of this tutorial and shown here too) in the file

    MyGameSurfaceView.java

    Shows the Class constructor as:

    public MyGameSurfaceView( Context context ) {
    super(context);
    // TODO ... stub
    }

    That is incorrect - it should be this:

    public MyGameSurfaceView(Context context, AttributeSet atributeSet) {
    super(context, atributeSet);
    // TODO ... stub
    }

    Tried on 2.2, 2.3, 4.x emulators and a couple of phones running 2.3.3 and 4.x

    cheers

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. hello HeisenBug,

    First of all, I thanks for your input. Any correction on my code is welcome, all your comments teach me so much.

    - I haven't delete your comment. Actually I haven't checked email to approval the comment, so it have not been shown.

    - I haven't corrected the code so far, the MyGameSurfaceView(Context context, AttributeSet attrs) constructor you menetioned is auto-generated in Eclipse when select something like implement super-class constructor...when create the class. So it should be here from day one.

    ReplyDelete
  5. Thank you for both replying and not deleting my post, which I know is not really happy, but is intended to be helpful. If you could correct this tutorial so that it works, many people like me would be grateful. thanks again...

    ReplyDelete
  6. onDraw(canvas) in updateSurfaceView shows error "suspecious method call"

    what is alternate?

    ReplyDelete

Infolinks In Text Ads