I used this tutorial to create a SurfaceView for a game. The tutorial fixed the problems I had with handling the thread but now whenever I open the fragment it takes anywhere between 3 and 15 seconds for the screen to turn yellow.
I thought it might have something to with the code in the container activity but the only thing I changed is this fragment alone. The speed before the change was fine.
public class VirtualMapFragment extends Fragment {
private MapSurfaceView mapSurfaceView;
#Override
public void onResume() {
if (mapSurfaceView != null){
mapSurfaceView.surfaceRestart();
}
super.onResume();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if(mapSurfaceView == null)
mapSurfaceView = new MapSurfaceView(getActivity());
return mapSurfaceView;
}
#Override
public void onPause() {
if (mapSurfaceView != null){
mapSurfaceView.surfaceDestroyed(mapSurfaceView.getHolder());
}
super.onPause();
}
private class MapSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceThread surfaceThread;
public MapSurfaceView(Context context) {
super(context);
setFocusable(true);
surfaceThread = new SurfaceThread(this);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
surfaceThread.restartSurface();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
surfaceThread.terminateSurface();
}
public void surfaceRestart(){
if (surfaceThread != null){
surfaceThread.restartSurface();
}
}
public void doDraw(Canvas canvas){
canvas.drawColor(Color.YELLOW);
}
#Override
public boolean onTouchEvent(MotionEvent event){
return false;
}
}
private class SurfaceThread implements Runnable {
private SurfaceHolder mSurfaceHolder;
private MapSurfaceView mMapSurfaceView;
private Canvas mCanvas;
volatile Thread thread;
public SurfaceThread(MapSurfaceView mapSurfaceView){
mSurfaceHolder = mapSurfaceView.getHolder();
mMapSurfaceView = mapSurfaceView;
mCanvas = new Canvas();
thread = new Thread(this);
thread.start();
}
public void terminateSurface(){
this.thread.interrupt();
}
public void restartSurface() {
if (thread.getState() == Thread.State.TERMINATED){
thread = new Thread(this);
thread.start(); // Start a new thread
}
}
public SurfaceHolder getSurfaceHolder() {
return mSurfaceHolder;
}
#Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
mCanvas = mSurfaceHolder.lockCanvas(null);
if (mCanvas == null){
mSurfaceHolder = mMapSurfaceView.getHolder();
} else {
synchronized (mSurfaceHolder) {
mapSurfaceView.doDraw(mCanvas);
}
}
} finally {
if (mCanvas != null) {
mSurfaceHolder.unlockCanvasAndPost(mCanvas);
}
}
}
}
}
}
Related
i use surfaceview for my game.
When the game is complete it'll automatically start other activity (score activity) by this intent :
mContext = getContext();
Intent intent = new Intent(mContext, ScoreScreen.class);
mContext.startActivity(intent);
it works fine, but has one problem when i want to close that score activity then back to surfaceview ("Try again" button).
i use this code to finish score activity:
tryagain.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
finish();
}
});
but it don't want back to surfaceview
Help me..
sorry for my bad english
this my SurfaceviewScreen :
public class SurfaceScreen extends Activity {
OurView v;
int level =1, lives = 3, score=0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
v = new OurView(this);
v.setKeepScreenOn(true);
setContentView(v);
}
#Override
protected void onPause() {
super.onPause();
v.pause();
}
#Override
protected void onResume() {
super.onResume();
v.resume();
}
public class OurView extends SurfaceView implements Runnable {
Thread t = null;
SurfaceHolder holder;
boolean isItOK = false;
boolean spriteLoaded = false;
private Context mContext;
Paint paint = new Paint();
public OurView(Context context) {
super(context);
this.mContext = context;
mContext = getContext();
holder = getHolder();
}
#Override
public void run() {
while (isItOK) {
if (!holder.getSurface().isValid()) {
continue;
}
update();
Canvas c = holder.lockCanvas();
onDraw(c);
holder.unlockCanvasAndPost(c);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void pause() {
isItOK = false;
while (true) {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
}
t = null;
}
public void resume() {
isItOK = true;
t = new Thread(this);
t.start();
}
protected void update(){
if(lives<1){
Intent i = new Intent(mContext, gameover.class);
i.putExtra("msg", "LOSE");
mContext.startActivity(i);
}
//updating
}
#Override
protected void onDraw(Canvas canvas) {
//drawing
}
}
}
}
and this is my ScoreScreen code :
public class ScoreScreen extends Activity{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.scorescreen);
TextView txtResult = (TextView)findViewById(R.id.scorescreen_result);
Button tryagain = (Button)findViewById(R.id.scorescreen_tryagain);
Intent i = this.getIntent();
txtResult.setText(i.getStringExtra("msg"));
tryagain.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
finish();
}
});
}
}
As far as I can uderstand your question you should try
ScoreActivity.this.finish();
I have got a problem
When hitting the home button, my game thread class gets paused. But when re-entering the app and the onResume() method gets called, it should resume the game thread... everythin i get is a force close.
Here is my MainActivity:
public class MainActivity extends Activity {
private RelativeLayout relativeLayout;
private GameView gameView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
gameView = new GameView(this, this);
setContentView(R.layout.activity_main);
relativeLayout = (RelativeLayout)findViewById(R.id.mainView);
relativeLayout.addView(gameView);
}
#Override
protected void onPause() {
super.onPause();
gameView.pause();
}
#Override
protected void onResume() {
super.onResume();
if(gameView.getGameThread() != null) {
if(gameView.getGameThread().isRunning == false) {
gameView.resume();
}
}
}
}
gameView is following class, a surfaceView getting rendered by GameThread:
public class GameView extends SurfaceView{
private GameThread gameThread;
public GameView(Context context, Activity activity) {
super(context);
gameThread = new GameThread(this);
holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
gameThread.setRunning(true);
gameThread.start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
});
}
public GameThread getGameThread() {
return gameThread;
}
public void update() {
}
public void pause() {
gameThread.pause();
}
public void resume() {
gameThread.resumeThread();
}
}
This is the GameThread class:
public class GameThread extends Thread{
private GameView view;
public boolean isRunning = false;
public GameThread(GameView view) {
this.view = view;
}
public void setRunning(boolean setRunning) {
isRunning = setRunning;
}
public void stopThread() {
isRunning = false;
}
public void run() {
while(isRunning) {
Canvas c = null;
view.update();
try {
c = view.getHolder().lockCanvas();
synchronized (view.getHolder()) {
view.draw(c);
}
}finally {
if(c != null) {
view.getHolder().unlockCanvasAndPost(c);
}
}
}
}
public void startThread() {
isRunning = true;
}
public void pause() {
isRunning = false;
}
public void resumeThread() {
isRunning = true;
}
}
I removed the irrelevant parts of the code.
When re-entering the app the android.view.Choreographer class opens
Any ideas?
Actually you're not pausing your thread (to be strict, there's no such thing as pausing a thread). You're ending your thread.
When you call pause() you set the isRunning flag to false, that makes the thread exit the loop and the thread ends. When you set your 'isRunning' to true again, the thread won't restart.
Actually you can't reuse threads in java, you have to create a new one, because once the thread loop ends, it's dead.
Hey everyone I am fairly new to the android/glass development so please correct me if I am wrong.
I tried to create a High Frequency Live Card which is fine. However when I close the app, the function surfaceDestroyed() did not get trigger and thread.quit() did not get called.
I tried looking into the sample project stopwatch/timer and apparently they did not stop the thread as well. According to the documentation, surfaceDestroyed gets called right before the surface is destroy but what is my surface? I also read something call surfaceview, are they the same thing? is surfaceview my customview or livecard?
https://developers.google.com/glass/develop/gdk/ui/live-cards#creating_high-frequency_live_cards
Appreciate any kind of help!!!
/ ***
* LiveCardRender Class
*** /
public class LiveCardRender implements DirectRenderingCallback {
private static final long FRAME_TIME_MILLIS = 33;
private CustomView mCustomView;
private SurfaceHolder mHolder;
private boolean mPaused;
private RenderThread mRenderThread;
private class RenderThread extends Thread {
private boolean mShouldRun;
public RenderThread() {
mShouldRun = true;
}
private synchronized boolean shouldRun() {
return mShouldRun;
}
public synchronized void quit() {
mShouldRun = false;
}
#Override
public void run() {
while (shouldRun()) {
draw(mCustomView);
SystemClock.sleep(FRAME_TIME_MILLIS);
}
}
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
mHolder = holder;
updateRendering();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
mHolder = null;
updateRendering();
}
#Override
public void renderingPaused(SurfaceHolder holder, boolean paused) {
mPaused = paused;
updateRendering();
}
private synchronized void updateRendering() {
boolean shouldRender = (mHolder != null) && !mPaused;
boolean rendering = mRenderThread != null;
if (shouldRender != rendering) {
if (shouldRender) {
mRenderThread = new RenderThread();
mRenderThread.start();
} else {
mRenderThread.quit();
mRenderThread = null;
}
}
}
private void draw(View view) {
Canvas canvas;
try {
canvas = mHolder.lockCanvas();
} catch (Exception e) {
return;
}
if (canvas != null) {
view.draw(canvas);
mHolder.unlockCanvasAndPost(canvas);
}
}
}
/ ***
* LaunchService Class that uses LiveCardRender to update the live card
*** /
public class LaunchService extends Service {
private static final String LIVE_CARD_TAG = "motion_card";
private TimelineManager mTimelineManager;
private LiveCard mLiveCard;
private LiveCardRender mLiveCardRender;
#Override
public void onCreate() {
mTimelineManager = TimelineManager.from(this);
}
public int onStartCommand(Intent intent, int flags, int startId) {
if (mLiveCard == null) {
mLiveCard = mTimelineManager.createLiveCard(LIVE_CARD_TAG);
mLiveCardRender = new LiveCardRender(this);
mLiveCard.setDirectRenderingEnabled(true);
mLiveCard.getSurfaceHolder().addCallback(mLiveCardRender);
mLiveCard.publish(PublishMode.REVEAL);
}
return START_STICKY;
}
#Override
public void onDestroy(){
if (mLiveCard != null && mLiveCard.isPublished()) {
if (mLiveCardRender != null) {
mLiveCard.getSurfaceHolder().removeCallback(mLiveCardRender);
}
mLiveCard.unpublish();
mLiveCard = null;
}
super.onDestroy();
}
}
It looks like the bug is here, inside onDestroy:
if (mLiveCard != null && mLiveCard.isPublished()) {
if (mLiveCardRender != null) {
mLiveCard.getSurfaceHolder().removeCallback(mLiveCardRender);
}
mLiveCard.unpublish();
By removing the callback before the unpublish method is called, the system no longer knows whose surfaceDestroyed method to call when the card is removed afterwards. You can actually remove call to removeCallback entirely; it's unnecessary.
It looks like this is a bug in some of our samples that we missed as our APIs evolved. Thanks for catching it and we'll have them updated shortly!
I am trying to open a camera preview but I get an
FATAL EXCEPTION: AsyncTask #1 java.lang.RuntimeException: An error
occured while executing doInBackground()
Below is my code:
public class ScanActivity extends Activity implements SurfaceHolder.Callback, OnClickListener {
private SurfaceHolder surface_holder;
protected Camera camera;
protected boolean scanning;
private Animation laser_effect;
private Animation flash_effect;
public static final class id {
public static final int quit = 0;
public static final int found = 1;
public static final int nothing = 2;
public static final int error = 3;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scan);
findViewById(R.id.capture_button).setOnClickListener(this);![enter image description here][1]
}
#Override
protected void onResume() {
super.onResume();
/* open camera if any, returns error otherwise */
camera = Camera.open();
if (camera == null) {
Intent error = new Intent();
error.putExtra("reason", "Camera Error");
setResult(id.error, error);
finish();
}
restartView();
surface_holder = ((SurfaceView) findViewById(R.id.surface_view)).getHolder();
surface_holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
surface_holder.addCallback(this);
}
#Override
protected void onPause() {
this.scanning=false;
/* release camera */
camera.cancelAutoFocus();
camera.stopPreview();
camera.release();
super.onPause();
}
private void restartView() {
findViewById(R.id.freeze).setVisibility(View.GONE);
findViewById(R.id.loading).setVisibility(View.GONE);
findViewById(R.id.capture_button).setVisibility(View.VISIBLE);
findViewById(R.id.surface_view).setVisibility(View.VISIBLE);
findViewById(R.id.camera_loading).setVisibility(View.GONE);
}
#Override
public void onClick(View v) {
switch(v.getId()) {
case R.id.capture_button:
scanning = true;
break;
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// void implementation
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
findViewById(R.id.camera_loading).setVisibility(View.VISIBLE);
//we use an AsyncTask to avoid ugly lag when camera is loading...
new InitCamera().execute(holder);
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// void implementation
}
private class InitCamera extends AsyncTask<SurfaceHolder, Void, Boolean> {
#Override
protected Boolean doInBackground(SurfaceHolder... holder) {
/* initialize camera parameters and start Preview */
Rect dim = holder[0].getSurfaceFrame();
int w = dim.width();
int h = dim.height();
Camera.Parameters params = camera.getParameters();
params.setPreviewSize(w, h);
camera.setParameters(params);
try {
camera.setPreviewDisplay(surface_holder);
} catch (IOException e) {
e.printStackTrace();
}
camera.startPreview();
return true;
}
#Override
protected void onPostExecute(Boolean a) {
findViewById(R.id.camera_loading).setVisibility(View.GONE);
}
}
}
Most likely, the problem comes from trying to manipulate the UI from a non-UI thread. Examining your stack trace would help confirm this theory. AFAIK, the work that you are doing in doInBackground() should be performed on the main application thread.
I have a game I'm developing for Android 2.x. When the player looses, I want to automatically exit that and go back to the previous menu of the game. This may be a simple question, but how do I do that?
I have a MenuActivity, which calls GameActivity, which calls the GameView (implementation of SurfaceView) where the game logic is.
UPDATE: No, wait, it's more complex than that. To make this game I followed the MoonLander example, which used threads.
GameView:
public class GameActivity extends Activity
{
GameView gameView;
GameThread gThread;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
gameView = new GameView(this);
setContentView(gameView);
gThread = gameView.getThread();
gThread.doStart();
}
}
GameView:
package com.cp.balldrop;
public class GameView extends SurfaceView implements SurfaceHolder.Callback
{
class GameThread extends Thread
{
public GameThread(SurfaceHolder sHolder, Context context, Handler handler)
{
//Code
}
public void doStart()
{
synchronized (mSurfaceHolder)
{
}
}
#Override
public void run()
{
while (running)
{
Canvas c = null;
try
{
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder)
{
doDraw(c);
}
}
finally
{
if (c != null)
{
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
protected void doDraw(Canvas canvas)
{
//code
}
}
/** Handle to the application context, used to e.g. fetch Drawables. */
private Context mContext;
/** Pointer to the text view to display "Paused.." etc. */
private TextView mStatusText;
/** The thread that actually draws the animation */
private GameThread gThread;
public GameView(Context context)
{
super(context);
// register our interest in hearing about changes to our surface
SurfaceHolder holder = getHolder();
holder.addCallback(this);
// create thread only; it's started in surfaceCreated()
gThread = new GameThread(holder, context, new Handler()
{
#Override
public void handleMessage(Message m)
{
// mStatusText.setVisibility(m.getData().getInt("viz"));
// mStatusText.setText(m.getData().getString("text"));
}
});
setFocusable(true);
}
public GameThread getThread()
{
return gThread;
}
public void surfaceCreated(SurfaceHolder holder)
{
gThread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder)
{
boolean retry = true;
gThread.setRunning(false);
while (retry)
{
try
{
gThread.join();
retry = false;
}
catch (InterruptedException e)
{
}
}
}
}
In my thread, I call the following method from my surfaceview's doTouch:
public void doLose() {
synchronized (mSurfaceHolder) {
//quit to mainmenu
((Activity) mContext).finish();
}
}
This seems to shut down the surfaceview and the activity hosting it, dumping me back in my main-menu activity. Note the casting and the predefined context.
What about calling finish() on the GameActivity?