When using Surface View to make a game, I have a problem. I have a MainThread class, and all my update functions are in the run() function. The condition is:
while(running){
//do anything
}
I want to stop playing game and resume that by 2 menu items. In "stop item" I set thread.setRunning(false) and in "resume item" I set thread.setRunning(true), but it keeps running anyway!
After that, I tried to put a boolean inside the loop like this:
while(running){
if(isPlaying == true){
//do anything
}
}
And if I want to stop, I set the boolean variable to false and true to resume. But that doesn't work either. What do I need to do?
Try this:
while(running){
if(isPlaying == true){
//do anything
} else break;
}
If you set false, the thread will end and never stop again, unless you create a new thread. Therefore, you need to add a new menu item to control "quit". The "running" is used to control "resume" and "pause". For example:
public class MainThread extends Thread{
volatile boolean isQuit = false;
volatile boolean running = false;
Object lock = new Object();
public void setRunning(boolean running) {
this.running = running;
synchronized (lock) {
lock.notify();
}
}
public void setQuit(boolean isQuit) {
this.isQuit = isQuit;
}
#Override
public void run() {
// TODO Auto-generated method stub
super.run();
while (!isQuit) {
if (!running) {
System.out.println("pause ganme");
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println(e);
break;
}
}
}
else {
// continue game
System.out.println("running ganme");
}
}
System.out.println("quit ganme");
}
public void quit() {
isQuit = true;
running = false;
synchronized (lock) {
lock.notify();
}
}
}
Related
I am trying to write a message to stdout in android every second for which a given button is held(I intend to place a method there later). I use switch in a standard onTouch method to detect button press:
protected void setFab(FloatingActionButton fab) {
fab.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
System.out.println("Button pressed");
handleButtonDown();
return true;
}
case MotionEvent.ACTION_UP: {
System.out.println("Button released");
handleButtonUp();
return true;
}
default:
return true;
}
}
});
}
And global two global booleans:
private boolean captureThreadRunning = false;
private boolean cancelCaptureThread = false;
Which are to stop the looping when the button is released:
public void handleButtonUp() {
cancelCaptureThread = true;
}
However, when I start the worker thread, it gets stuck in an infinite loop, even when the button is released and thus the global boolean should be changed:
public void handleButtonDown() {
System.out.println("Capture thread running: " + captureThreadRunning);
if (!captureThreadRunning) {
System.out.println("Thread starting");
startCaptureThread();
}
}
public void startCaptureThread() {
System.out.println("Thread started");
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
try {
captureThreadRunning = true;
System.out.println("Got to try");
while (!cancelCaptureThread) {
System.out.println("Success");
try {
System.out.println("Falling asleep");
Thread.sleep(1000);
System.out.println("Slept 1000");
} catch (InterruptedException e) {
throw new RuntimeException(
"Interrupted.", e);
}
}
} finally {
System.out.println("got to finally");
captureThreadRunning = false;
cancelCaptureThread = false;
}
}
});
thread.run();
}
Not only that, but the UI also gets frozen, which it certainly shouldn't, cause I am doing everything in a separate thread. When I release the button, the loop should halt, because the boolean gets changed.
I am quite new to both threads and android, so I guess I am just missing something.
But you are indeed executing your code in your main thread. Notice that you are calling
thread.run();
so your execution path enters into an infinite loop. Change it to
thread.start();
That will start a new Thread
I am developing an app that needs to perform network tasks one-by-one in a particular order, and with a particular time in between each execution.
I have tried to implement this using AsyncTask and TimerTask.
AsyncTask won't work because to be able to cancel it I need to create an AsyncTask object, but if I do that then I can't re-launch the task upon completion.
TimerTask works to some extent, but it is extremely clunky. Trying to cancel a TimerTask mid-operation has proven to be quite difficult, and I am constantly getting two versions of my task running. This problem amplified when I tried to split the TimerTask into five smaller TimerTasks (one for each operation that needs to be completed).
So, is there any way to perform a set of background tasks, in order, with a particular time in between each execution?
import java.util.ArrayList;
import java.util.Collections;
public class RunnableQueue {
ArrayList<Runnable> queue;
Runnable[] tasks;
boolean cancelled;
double tag;
public RunnableQueue(Runnable... tasks) {
this.tasks = tasks;
this.tag = Math.random();
queue = new ArrayList<Runnable>();
Collections.addAll(queue, tasks);
}
public void add(Runnable r) {
queue.add(r);
}
public double getTag() {
return tag;
}
public void addToFront(Runnable r) {
queue.add(0, r);
}
public void next() {
if (queue.size() > 0) {
new Thread(queue.get(0)).start();
queue.remove(0);
}
}
public void stop() {
cancelled = true;
queue.clear();
}
public void resume() {
if (cancelled) {
tag = Math.random();
cancelled = false;
Collections.addAll(queue, tasks);
next();
}
}
public boolean isCancelled() {
return cancelled;
}
}
Here's an example Runnable that would work with the RunnableQueue:
Runnable queueTester = new Runnable() {
#Override
public void run() {
double tag = executor.getTag();
//do some background stuff here
Log.i("RunnableQueue-test", "queueTester complete. Sleeping...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (executor.isCancelled()) {
Log.i(TAG, "Boolean is false. Not continuing.");
} else if (executor.getTag() != tag) {
Log.i(TAG, "Tag has been updated. Not continuing.");
} else {
executor.add(this);
executor.next();
}
}
};
I want to make stopwatch. And i create stopwatch class like this. And when i call onPause in another Activity its freeze application.
public class StopWatch implements Runnable {
private Object mPauseLock;
private boolean mPaused;
private boolean mFinished;
private ArrayList<TextView> textFields;
private Handler mHandler = new Handler();
public StopWatch( ArrayList<TextView> textFields) {
mPauseLock = new Object();
mPaused = false;
mFinished = false;
this.textFields =textFields;
}
public void run() {
textFields.get(1).setText("progressing...");
if (!mPaused) {
mHandler.postDelayed(this, 1000);
}
synchronized (mPauseLock) {
while (mPaused) {
try {
mPauseLock.wait();
} catch (InterruptedException e) {
}
}
}
}
public void onPause() {
synchronized (mPauseLock) {
mPaused = true;
}
}
public void onResume() {
synchronized (mPauseLock) {
mPaused = false;
mPauseLock.notifyAll();
}
}
}
and i create instance of class in another View like. Can somebody exmplain me where is problem?
stopky = new StopWatch(textFields);
stopky.run();
// do another stuff and register buttons with onClickListener and call
stopky.onPause(); // freeze application
stopky.onResume();
You can't Object.wait() in a run method called from a Handler, which is probably running on the main/UI Thread.
The whole Android app is coordinated via short methods which register with the main/UI Thread. You're probably registering your stopwatch there, too. It's not possible to perform a while loop there and at the same time process events from the user interface..
A quick solution would be to re-schedule your run method and check the status the next time it gets called. Basically like so:
public void run() {
textFields.get(1).setText("progressing...");
if (!mPaused) {
// do what has to be done when stopwatch is running
mHandler.postDelayed(this, 1000);
} else {
// just re-schedule with a shorter delay
mHandler.postDelayed(this, 10);
}
}
An even better way would be to go for a fully event-driven design and avoid calling the stopwatch at all while it is stopped. In this case, you would simply re-start it from the Button's event handler.
I have a problem with a thread in surfaceview. I can't understand how to onPause/onResume when I lock my phone. Whatever I do, the thread doesn't respond after locking/unlocking the phone.
In the activity:
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
surfaceView.SurfaceView_OnResume();
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
surfaceView.SurfaceView_OnPause();
}
In the surfaceview
public void SurfaceView_OnResume() {
if (null != surfaceViewThread) {
surfaceViewThread.setRunning(true);
surfaceViewThread.notify();
}
}
public void MySurfaceView_OnPause() {
surfaceViewThread.setRunning(false);
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
boolean retry = true;
myGameThread.setRunning(false);
while (retry) {
try {
myGameThread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
In the thread:
public void setRunning(boolean run) {
runFlag = run;
}
Thread.setRunning(boolean b) is not in the Android API.
You can check it here. http://developer.android.com/reference/java/lang/Thread.html
And if I need a Thread, I prefer to use Runnable(Interface), not Thread(Object).
To control my Thread cycle, I will design my method:run() like this...
run(){
while(threadRun){
...//What you want to do in the thread.
while(threadPause){
}
}
}
The Boolean:threadRun will turn to false in Activity.onDestroy(), or any other time you really want to shut down the thread.
The Boolean:threadPause ... turn to false in Activity.onPause() and turn to true in Activity.onResume().
i have a thread problem with my surfaceView, its quite common but i have not found a solution anywhere.
I use a SurfaceView as a Background in a frame layout:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/blobmusic_layout"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.myapp.Background
android:id="#+id/background_blobmusic"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
...
and my background class is the tipical surfaceview class, with the holder, the thread wich will call our onDraw method, etc. Some code to avoid questions (init() is called in the constructor methods):
public void init() {
ready = false;
holder = getHolder();
holder.addCallback( new Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
boolean retry = true;
while (retry) {
try {
bgdt.setRunning(false);
bgdt.join();
retry = false;
} catch (InterruptedException e) {}
}
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
holder.setFormat(PixelFormat.RGBA_8888);
estrelles = new Estrelles(getResources(),getHeight());
bgdt.setRunning(true);
bgdt.start();
ready = true;
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
});
and my thread class (bgtd):
public class BGDrawThread extends Thread {
private Background view;
static final long FPS = 60;
private boolean running = false;
public BGDrawThread(Background view) {
this.view = view;
}
public boolean isRunning() {
return running;
}
public void setRunning(boolean run) {
running = run;
}
#Override
public void run() {
long ticksPS = 1000/FPS;
long startTime;
long sleepTime;
//fps checker
long contms=0;
long lasttimecheck = System.currentTimeMillis();
int fps=0;
while (running) {
if(contms>1000) {
//souts ??
contms=0;
fps=0;
lasttimecheck = System.currentTimeMillis();
}
else {
fps++;
contms+=System.currentTimeMillis()-lasttimecheck;
lasttimecheck=System.currentTimeMillis();
}
Canvas c = null;
startTime = System.currentTimeMillis();
try {
c = view.getHolder().lockCanvas();
synchronized (view.getHolder()) {
view.onDraw(c);
}
} finally {
if (c != null) {
view.getHolder().unlockCanvasAndPost(c);
}
}
sleepTime = ticksPS-(System.currentTimeMillis() - startTime);
try {
if (sleepTime > 0)
sleep(sleepTime);
else
sleep(10);
} catch (Exception e) {}
}
}
}
This works fine, until another activity comes to front and then we come back from it, that crashes.
Here's my code of onpause/onresume methods of the class:
#Override
protected void onResume() {
super.onResume();
Background bmbg = (Background) findViewById(R.id.background_blobmusic);
bmbg.resumethread();
}
#Override
protected void onPause() {
super.onPause();
Background bmbg = (Background) findViewById(R.id.background_blobmusic);
bmbg.stopthread();
}
and the strop/resume thread in the surfaceview class:
public void stopthread() {
if(!bgdt.isAlive()) return;
boolean retry = true;
while (retry) {
try {
bgdt.setRunning(false);
bgdt.join();
retry = false;
} catch (InterruptedException e) {}
}
}
public void resumethread() {
if(ready && !bgdt.isRunning()) {
bgdt.setRunning(true);
bgdt.start();
}
}
All help is welcome :)
EDIT: i made it run (probably not an elegant solution, but i leave the code here):
(look at the comment below to see the other changes)
public void startthread() {
if(!surfacecreated) return;
if(threadrunning) return;
bgdt = new BGDrawThread(this);
bgdt.setRunning(true);
bgdt.start();
threadrunning = true;
}
public void stopthread() {
boolean retry = true;
while (retry) {
try {
bgdt.setRunning(false);
bgdt.join();
threadrunning = false;
retry = false;
} catch (InterruptedException e) {}
}
}
IIRC surface is ready for manipulations only after surfaceChanged() callback, not surfaceCreated() , and surfaceChanged() is called after onResume() - move your initialisation and thread startup there ( to surfaceChanged() )