I have a class extending Thread. Inside this run I have a few method working in while loop. Now I want one of this methods gameSurface.updateBitmapObjects() to work with delay. How to achieve this? Here is my code:
GameThread.java:
public class GameThread<T extends GameSurface> extends Thread {
private boolean isRunning;
private long startTime, loopTime;
private long delay = 33;
private SurfaceHolder surfaceHolder;
private T gameSurface;
public GameThread(SurfaceHolder surfaceHolder, T gameSurface) {
this.surfaceHolder= surfaceHolder;
this.gameSurface = gameSurface;
isRunning = true;
}
#Override
public void run() {
while(isRunning) {
startTime = SystemClock.uptimeMillis();
Canvas canvas = surfaceHolder.lockCanvas(null);
if(canvas != null) {
synchronized (surfaceHolder) {
gameSurface.updateBitmapObjects();
gameSurface.drawMapBitmap(canvas);
((GameMainSurface)gameSurface).drawCoinAndCoins();
((GameMainSurface)gameSurface).drawFieldLines(canvas);
((GameMainSurface)gameSurface).updatePlayersLabels(canvas);
if (((GameMainSurface)gameSurface).isGameMainFragment()) {
((GameMainSurface)gameSurface).setSomeValues();
}
gameSurface.drawObjects(canvas);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
loopTime = SystemClock.uptimeMillis() - startTime;
if(loopTime < delay) {
try {
Thread.sleep(delay - loopTime);
}
catch (InterruptedException e) {
Log.e("Interupted ex", e.getMessage());
}
}
}
}
}
GameMainSurface.java:
(...)
public void updateBitmapObjects() {
this.chibi1.update();
(...)
}
MovingObject.java:
// I want this method to work with delay let's say 100 ms
public void update() {
this.colUsing++;
if (colCount == 13) {
if (colUsing >= 9) {
if (rowUsing == ROW_TOP_TO_BOTTOM_CHIBI || rowUsing == ROW_BOTTOM_TO_TOP_CHIBI)
colUsing = 1;
else
colUsing = 0;
}
}
if (colCount == 4) {
if (colUsing >= 4) {
this.colUsing = 0;
}
}
}
Related
I'm having difficulties pausing and resuming my game. I am presented with an error IllegalThreadStateException, but what is interesting here is that this code runs well and without issues on API 24. Testing on higher API's is where I run into troubles. Am I missing something? Any tips? I'm still a beginner so thank you all!
public class GameLoop extends Thread {
private final SurfaceHolder surfaceHolder;
private Game game;
public static final float MAX_UPS = 60;
private static final double UPS_PERIOD = 1E+3 / MAX_UPS;
public static boolean isRunning = false;
private double avarageUPS;
private double avarageFPS;
public GameLoop(Game game, SurfaceHolder surfaceHolder) {
this.game = game;
this.surfaceHolder = surfaceHolder;
}
public void startLoop() {
Log.d("GameLoop.java", "startLoop()");
isRunning = true;
start();
}
public void stopLoop() {
Log.d("GameLoop.java", "stopLoop()");
isRunning = false;
// Wait for thread to join
try {
join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
#Override
public void run() {
Log.d("GameLoop.java", "run()");
super.run();
//Declare time and cycle count variables
int updateCount = 0;
int frameCount = 0;
long startTime;
long elapsedTime;
long sleepTime;
//Game loop
Canvas canvas = null;
startTime = System.currentTimeMillis();
while(isRunning){
//Poskusi posodobiti in render game
try{
canvas = surfaceHolder.lockCanvas();
synchronized (surfaceHolder){
game.update();
updateCount++;
game.draw(canvas);
}
}catch(IllegalArgumentException e){
e.printStackTrace();
}finally {
if(canvas != null){
try{
surfaceHolder.unlockCanvasAndPost(canvas);
frameCount++;
}catch(Exception e) {
e.printStackTrace();
}
}
}
//Pause game loop to not exceed target UPS
elapsedTime = System.currentTimeMillis() - startTime;
sleepTime = (long) (updateCount * UPS_PERIOD - elapsedTime);
if(sleepTime > 0){
try {
sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//Skip frames to keep up with target UPS
while(sleepTime < 0 && updateCount < MAX_UPS-1){
game.update();
updateCount++;
elapsedTime = System.currentTimeMillis() - startTime;
sleepTime = (long) (updateCount * UPS_PERIOD - elapsedTime);
}
//Calculate avarage UPS in FPS
elapsedTime = System.currentTimeMillis() - startTime;
if(elapsedTime >= 1000){
avarageUPS = updateCount / (1E-3 * elapsedTime);
avarageFPS = frameCount / (1E-3 * elapsedTime);
updateCount = 0;
frameCount = 0;
startTime = System.currentTimeMillis();
}
}
}
public double getAvarageUPS() {
return avarageUPS;
}
public double getAvarageFPS() {
return avarageFPS;
}
}
If you're trying to call start again on the same thread, you can't. After a thread finishes, you'd need to create a new instance, the same instance can't be rerun.
There is something wrong with my code. The timer overall seems to be working fine and the pause button does its job.
The problem is when you pause the clock at a specific time and then you unpause it.
If we (let's say) pause it at 8 seconds and we unpause it after a minute, it doesn't keep going like 9-10-11, etc. It goes 74-75-76... (I've broken it into minutes and seconds).
Is it a thread that causes the problem? (Also, I've overused freeze_sec and freeze_min time code snippets just to see if it would be fixed but it wasn't.)
Here is the code:
Thread t1 = null;
ss = new ServerSocket(6800);
while(true) {
s = ss.accept();
isr = new InputStreamReader(s.getInputStream());
br = new BufferedReader(isr);
message = br.readLine();
if (message.equals("START")) {
t1 = new Thread(new Thread1());
t1.start();
...
} else if (message.equals("PAUSE")) {
if(check) {
try {
check = false;
Thread1.PAUSE(true);
} catch (Exception e) {
System.out.println("Exception e");
}
} else {
check = true;
Thread1.PAUSE(false);
}
}
And Thread1 class looks like:
import java.io.*;
import java.util.Date;
import java.util.Scanner;
public class Thread1 extends MyServerFrame implements Runnable{
private static int current_min_time = 0;
private static int current_sec_time = 0;
private static int freeze_min_time = 0;
private static int freeze_sec_time = 0;
private static boolean pause = false;
private static int minutes = 0;
private int total_time_sec = 0;
private static boolean freeze_signal = false;
private static int k = 0;
#Override
public void run() {
long elapsedTime = 0L;
boolean bool = true;
int num = 0;
while (bool) {
Scanner scan = new Scanner(System.in);
if (minutes == 0) {
System.out.println("How many minutes for this half-time?");
Scanner in = new Scanner(System.in);
num = in.nextInt();
minutes = num;
}
long startTime = System.currentTimeMillis();
while (total_time_sec < minutes * 60 || freeze_signal == false) {
if (freeze_signal && k == 0) {
freeze_sec_time = current_sec_time;
freeze_min_time = current_min_time;
k++;
}
if (!pause) {
//perform db poll/check
if (elapsedTime / 1000 != current_sec_time) {
try {
clearTheFile("Half_Time.txt");
} catch (IOException e) {
System.out.println("Exception");
}
if (!freeze_signal && k > 0) {
current_sec_time = freeze_sec_time;
current_min_time = freeze_min_time;
k = 0;
}
current_sec_time++;
total_time_sec = current_sec_time + current_min_time / 60;
print_in_txt();
}
elapsedTime = (new Date()).getTime() - startTime;
if (current_sec_time == 60) {
if (!freeze_signal && k > 0) {
current_sec_time = freeze_sec_time;
current_min_time = freeze_min_time;
try {
clearTheFile("Half_Time.txt");
} catch (IOException e) {
System.out.println("Exception");
}
print_in_txt();
k = 0;
}
current_sec_time = 0;
current_min_time++;
total_time_sec = current_sec_time + current_min_time / 60;
startTime = System.currentTimeMillis();
elapsedTime = (new Date()).getTime() - startTime;
try {
clearTheFile("Half_Time.txt");
} catch (IOException e) {
System.out.println("Exception");
}
print_in_txt();
}
}
}
}
}
public static void clearTheFile(String txt_name) throws IOException {
try {
FileWriter fwOb = new FileWriter(txt_name, false);
PrintWriter pwOb = new PrintWriter(fwOb, false);
pwOb.flush();
pwOb.close();
fwOb.close();
} catch (IOException e) {}
}
public static void print_in_txt() {
PrintWriter out;
try {
out = new PrintWriter("Half_Time.txt");
out.println(String.format("%02d", current_min_time) + ":" + String.format("%02d", current_sec_time));
out.print("");
out.close();
} catch (FileNotFoundException e) {
System.err.println("File doesn't exist");
e.printStackTrace();
}
}
public static void PAUSE(boolean p) {
if (p) {
pause = true;
freeze_signal = true;
} else {
current_sec_time = freeze_sec_time;
current_min_time = freeze_min_time;
try {
clearTheFile("Half_Time.txt");
} catch (IOException e) {
System.out.println("Exception");
}
print_in_txt();
pause = false;
freeze_signal = false;
}
}
}
So, after spending some time bagging my head against the idea, I suddenly realised that you don't actually need the thread at all.
What you need is a way to calculate the duration between to points in time, which doesn't need a thread to update the state, it's done automatically.
The thread is just doing "other stuff"
So, based on that, I took a StopWatch class from one of my previous answers...
public class StopWatch {
private Instant startTime;
private Duration totalRunTime = Duration.ZERO;
public StopWatch start() {
startTime = Instant.now();
return this;
}
public StopWatch stop() {
Duration runTime = Duration.between(startTime, Instant.now());
totalRunTime = totalRunTime.plus(runTime);
startTime = null;
return this;
}
public StopWatch pause() {
return stop();
}
public StopWatch resume() {
return start();
}
public StopWatch reset() {
stop();
totalRunTime = Duration.ZERO;
return this;
}
public boolean isRunning() {
return startTime != null;
}
public Duration getDuration() {
Duration currentDuration = Duration.ZERO;
currentDuration = currentDuration.plus(totalRunTime);
if (isRunning()) {
Duration runTime = Duration.between(startTime, Instant.now());
currentDuration = currentDuration.plus(runTime);
}
return currentDuration;
}
}
And applied so it could be used within a Thread, which would simply print the running time.
Around this, I added the ability to pause, resume and stop the thread so as to demonstrate the basic idea...
public class StopWatchRunnable implements Runnable {
private final Lock pauseLock = new ReentrantLock();
private final Condition pauseCondtion = pauseLock.newCondition();
private final AtomicBoolean isPaused = new AtomicBoolean(false);
private final AtomicBoolean isRunning = new AtomicBoolean(true);
private final StopWatch stopWatch = new StopWatch();
#Override
public void run() {
stopWatch.start();
while (isRunning.get()) {
while (isPaused.get()) {
pauseLock.lock();
stopWatch.pause();
try {
pauseCondtion.await();
} catch (InterruptedException ex) {
} finally {
pauseLock.unlock();
stopWatch.resume();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
}
Duration duration = stopWatch.getDuration();
String formatted = String.format("%dhrs %02dmins, %02dseconds", duration.toHours(), duration.toMinutesPart(), duration.toSecondsPart());
System.out.println(formatted);
}
}
public void stop() {
pauseLock.lock();
try {
isPaused.set(false);
isRunning.set(false);
} finally {
pauseCondtion.signalAll();
pauseLock.unlock();
}
}
public void pause() {
pauseLock.lock();
try {
isPaused.set(true);
} finally {
pauseLock.unlock();
}
}
public void resume() {
pauseLock.lock();
try {
isPaused.set(false);
} finally {
pauseCondtion.signalAll();
pauseLock.unlock();
}
}
}
Runnable example...
This basically takes the code from above and dumps it into a simple runnable example which demonstrates the pause/resume functionality
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class StopWatchExample {
public static void main(String[] args) throws InterruptedException {
new StopWatchExample();
}
public StopWatchExample() throws InterruptedException {
StopWatchRunnable stopWatch = new StopWatchRunnable();
Thread thread = new Thread(stopWatch);
thread.start();
Thread.sleep(5000);
System.out.println("Pause...");
stopWatch.pause();
Thread.sleep(5000);
System.out.println("Resume...");
stopWatch.resume();
Thread.sleep(5000);
System.out.println("Stop...");
stopWatch.stop();
thread.join();
System.out.println("All done...");
}
public class StopWatch {
private Instant startTime;
private Duration totalRunTime = Duration.ZERO;
public StopWatch start() {
startTime = Instant.now();
return this;
}
public StopWatch stop() {
Duration runTime = Duration.between(startTime, Instant.now());
totalRunTime = totalRunTime.plus(runTime);
startTime = null;
return this;
}
public StopWatch pause() {
return stop();
}
public StopWatch resume() {
return start();
}
public StopWatch reset() {
stop();
totalRunTime = Duration.ZERO;
return this;
}
public boolean isRunning() {
return startTime != null;
}
public Duration getDuration() {
Duration currentDuration = Duration.ZERO;
currentDuration = currentDuration.plus(totalRunTime);
if (isRunning()) {
Duration runTime = Duration.between(startTime, Instant.now());
currentDuration = currentDuration.plus(runTime);
}
return currentDuration;
}
}
public class StopWatchRunnable implements Runnable {
private final Lock pauseLock = new ReentrantLock();
private final Condition pauseCondtion = pauseLock.newCondition();
private final AtomicBoolean isPaused = new AtomicBoolean(false);
private final AtomicBoolean isRunning = new AtomicBoolean(true);
private final StopWatch stopWatch = new StopWatch();
#Override
public void run() {
stopWatch.start();
while (isRunning.get()) {
while (isPaused.get()) {
pauseLock.lock();
stopWatch.pause();
try {
pauseCondtion.await();
} catch (InterruptedException ex) {
} finally {
pauseLock.unlock();
stopWatch.resume();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
}
Duration duration = stopWatch.getDuration();
String formatted = String.format("%dhrs %02dmins, %02dseconds", duration.toHours(), duration.toMinutesPart(), duration.toSecondsPart());
System.out.println(formatted);
}
}
public void stop() {
pauseLock.lock();
try {
isPaused.set(false);
isRunning.set(false);
} finally {
pauseCondtion.signalAll();
pauseLock.unlock();
}
}
public void pause() {
pauseLock.lock();
try {
isPaused.set(true);
} finally {
pauseLock.unlock();
}
}
public void resume() {
pauseLock.lock();
try {
isPaused.set(false);
} finally {
pauseCondtion.signalAll();
pauseLock.unlock();
}
}
}
}
I have been writing a race code for a class I am in that races two threads, a tortoise and a hare. I can get both of them to run for 80 units but I don't know how to write a code that determines and outputs who the winner is. Any help would be appreciated because I am super new to coding.
I have the tortoise, hare, and raceParticipant classes. My driver class looks like this, where I would assume I put the winner code?
package Domain;
public class Driver
{
public static void main(String[] args)
{
Hare bob = new Hare();
Tortoise fred = new Tortoise();
int winDistance = 80;
do {
bob.sprint();
fred.sprint();
bob.display();
fred.display();
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}while(bob.getTotalDistance() < winDistance && fred.getTotalDistance() < winDistance);
}
}
My sprint method is
public int sprint()
{
int sleep = generator.nextInt(100);
int sprintDistance = 0;
if (sleep > sleepPercent)
{
sprintDistance = generator.nextInt(topSpeed) + 1;
}
totalDistance +=sprintDistance;
return sprintDistance;
}
I don't see you creating a new thread anywhere.
You can create a Hare class like this:
public class Hare implements Runnable {
private static final int SLEEP_DURATION = 3000; //milliseconds
private static final int SPEED = 3; //units per second
private int distanceToRun;
private final RaceFinishListener listener;
public Hare(int distanceToRun, RaceFinishListener listener) {
this.distanceToRun = distanceToRun;
this.listener = listener;
}
#Override
public void run() {
do {
distanceToRun -= SPEED;
try {
Thread.sleep(SLEEP_DURATION);
} catch (InterruptedException e) {
e.printStackTrace();
}
} while (distanceToRun > 0);
listener.onRaceFinished(getClass().getSimpleName());
}
}
and a similar Tortoise class with these variables:
private static final int SLEEP_DURATION = 1000; //sleeps less
private static final int SPEED = 1; //but is slow
Then create a listener to get notified when someone has finished:
public interface RaceFinishListener {
void onRaceFinished(String finisher);
}
and finally your main class:
public class Test implements RaceFinishListener {
private String winner;
private static final int DISTANCE_TO_RUN = 10;
public static void main(String[] args) {
new Test().race();
}
private void race() {
Hare bob = new Hare(DISTANCE_TO_RUN, this);
Tortoise fred = new Tortoise(DISTANCE_TO_RUN, this);
new Thread(bob).start();
new Thread(fred).start();
}
#Override
public void onRaceFinished(String finisher) {
synchronized (this) {
if (winner == null) {
winner = finisher;
System.out.println(finisher + " is the winner!");
} else {
System.out.println(finisher + " lost.");
}
}
}
}
Output
Tortoise is the winner!
Hare lost.
After this line:
}while(bob.getTotalDistance() < winDistance && fred.getTotalDistance() < winDistance);
You would just have:
boolean bobWins = (bob.getTotalDistance() >= winDistance);
boolean fredWins = (fred.getTotalDistance() >= winDistance);
if (bobWins && fredWins) {
System.out.println("It's a tie");
}
else if (bobWins) {
System.out.println("Bob Wins");
}
else {
System.out.println("Fred Wins");
}
I am not sure how to get this working. I want one this tread to stop once (startNumber == 0 || !Player.cCounting). !Player.cCounting is a boolean that is accessed. If either condition is met I want the loop to stop.
public class Console {
static long lastTime;
static boolean cCounting = true;
static long seconds = 0;
static long delta = 0;
static int startNumber = (int) Level.cookieTime / 1000;
Thread countDown;
public Console() {
cookieCountDown();
lastTime = System.currentTimeMillis();
}
public void cookieCountDown() {
countDown = new Thread(new Runnable() {
#Override
public void run() {
startNumber = (int) Level.cookieTime / 1000;
while (startNumber != 0) {
cCounting = Game.enter;
//System.out.println(cCounting);
try {
Thread.sleep(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
while (startNumber > 0 && cCounting) {
long now = System.currentTimeMillis();
delta = now - lastTime;
if (delta >= 1000) {
delta = 0;
lastTime = System.currentTimeMillis();
System.out.println("cookie " + startNumber);// print countdown;
startNumber--;
System.out.println(Player.cCounting);
if (Player.cCounting = false) {
end();
}
}
}
}
}
});
countDown.start();
}
public void end() {
System.out.println(startNumber);
System.out.println(cCounting);
if (startNumber == 0 || !Player.cCounting) {
System.out.println("stop");
//countDown.interrupt();
//countDown.stop();
}
}
}
You just need to exit your loop. Replace your end(); method call with return;. and make startNumber and Player.cCounting volatile e.g. volatile static int startNumber
I would like to get some advice on a simple multithreading system I am designing.
The idea:
The application is capturing frames and displaying them in 1st imageview. These captured frames are also being processed (by MyHandDetectionThread) and then displayed in 2nd imageview.
My solution:
public class VideoManager {
private volatile BufferLinkedList<InputFrame> mInputFrames;
private volatile BufferLinkedList<ProcessedFrame> mProcessedFrames;
private static VideoManager mVideoManagerInstance = new VideoManager();
private Timer captureTimer;
private MyVideoCaptureThread myVideoCaptureThread;
private MyFrameDisplayThread myFrameDisplayThread;
private MyHandDetectionThread myHandDetectionThread;
private MyProcessedFrameDisplayThread myProcessedFrameDisplayThread;
private enum ThreadMessages {
PROCESS_INPUT_FRAME,
NEW_INPUT_FRAME,
NEW_PROCESSED_FRAME_ARRIVED,
GET_NEW_FRAME
}
public static VideoManager getInstance() {
if (mVideoManagerInstance == null) {
mVideoManagerInstance = new VideoManager();
}
return mVideoManagerInstance;
}
// not visible constructor - for singleton purposes
private VideoManager() {
mInputFrames = new BufferLinkedList<>(Config.inputFramesListLimit);
mProcessedFrames = new BufferLinkedList<>(Config.inputFramesListLimit);
}
public void startDetectionAndRecognition(ImageView camIV, ImageView handIV) {
mInputFrames = new BufferLinkedList<>(Config.inputFramesListLimit);
mProcessedFrames = new BufferLinkedList<>(Config.inputFramesListLimit);
captureTimer = new Timer();
myVideoCaptureThread = new MyVideoCaptureThread();
myFrameDisplayThread = new MyFrameDisplayThread(camIV, handIV);
myHandDetectionThread = new MyHandDetectionThread();
myProcessedFrameDisplayThread = new MyProcessedFrameDisplayThread();
captureTimer.schedule(new TimerTask() {
public void run() {
if (myVideoCaptureThread != null && myVideoCaptureThread.threadMessages != null)
myVideoCaptureThread.threadMessages.offer(ThreadMessages.GET_NEW_FRAME);
}
}, 0, 1000 / Config.fps);
myFrameDisplayThread.start();
myVideoCaptureThread.start();
myHandDetectionThread.start();
myProcessedFrameDisplayThread.start();
}
public void stop() {
captureTimer.cancel();
myVideoCaptureThread.interrupt();
myHandDetectionThread.interrupt();
myFrameDisplayThread.interrupt();
myGestureRecogitionThread.interrupt();
mInputFrames.removeAll(mInputFrames);
mProcessedFrames.removeAll(mProcessedFrames);
isActive = false;
}
public boolean isActive() {
return isActive;
}
////////////////////////
// Thread clases
////////////////////////
private class MyVideoCaptureThread extends Thread {
LinkedBlockingQueue<ThreadMessages> threadMessages = new LinkedBlockingQueue<>(128);
#Override
public void run() {
WebCamVideoCapture vc = new WebCamVideoCapture();
while (!isInterrupted()) {
if (threadMessages != null && threadMessages.poll() == ThreadMessages.GET_NEW_FRAME) {
Mat mat = vc.getNextMatFrame();
if (mat != null && mInputFrames != null) {
mInputFrames.offerFirst(new InputFrame(mat));
if (myFrameDisplayThread != null && myFrameDisplayThread.threadMessages != null)
myFrameDisplayThread.threadMessages.offer(ThreadMessages.NEW_INPUT_FRAME);
if (myHandDetectionThread != null && myHandDetectionThread.threadMessages != null)
myHandDetectionThread.threadMessages.offer(ThreadMessages.PROCESS_INPUT_FRAME);
}
}
}
vc.close();
}
}
private class MyFrameDisplayThread extends Thread {
LinkedBlockingQueue<ThreadMessages> threadMessages = new LinkedBlockingQueue<>(128);
ImageView mCamImageView;
long lastUpdatedCamImageViewMillis;
long lastUpdatedHandImageViewMillis;
public MyFrameDisplayThread(ImageView mImageView) {
this.mCamImageView = mImageView;
}
private synchronized void updateImageViews() {
if (threadMessages.poll() == ThreadMessages.NEW_INPUT_FRAME && mInputFrames != null && !mInputFrames.isEmpty() && mInputFrames.peek() != null && mInputFrames.peek().getFrame() != null) {
if(Config.IS_DEBUG) System.out.println("Updating frame image view");
mCamImageView.setImage(Utils.cvMatToImage(mInputFrames.peekFirst().getFrame()));
}
}
#Override
public void run() {
while (!isInterrupted()) {
updateImageViews();
}
}
}
private class MyHandDetectionThread extends Thread {
LinkedBlockingQueue<ThreadMessages> threadMessages = new LinkedBlockingQueue<>(128); //TODO if multiple threads, define it out of class
HandDetector hd = new HandDetector();
#Override
public void run() {
while (!isInterrupted()) {
if (threadMessages.poll() == ThreadMessages.PROCESS_INPUT_FRAME && mInputFrames != null && mInputFrames.size() > 0 && mInputFrames.peek() != null) {
if(Config.IS_DEBUG) System.out.println("Detecting hand...");
mProcessedFrames.offerFirst(new ProcessedFrame(hd.detectHand(mInputFrames.peek()), null, null, null));
if (myGestureRecogitionThread != null && myGestureRecogitionThread.threadMessages != null)
myGestureRecogitionThread.threadMessages.offer(ThreadMessages.NEW_PROCESSED_FRAME_ARRIVED);
if(myFrameDisplayThread != null && myFrameDisplayThread.threadMessages != null)
myFrameDisplayThread.threadMessages.offer(ThreadMessages.NEW_PROCESSED_FRAME_ARRIVED);
}
}
}
}
private class MyProcessedFrameDisplayThread extends Thread {
LinkedBlockingQueue<ThreadMessages> threadMessages = new LinkedBlockingQueue<>(128);
ImageView mHandImageView;
public MyProcessedFrameDisplayThread(ImageView mHandImageView) {
mHandImageView = mHandImageView;
}
private synchronized void updateImageViews() {
if(threadMessages.poll() == ThreadMessages.NEW_PROCESSED_FRAME_ARRIVED && mProcessedFrames != null && !mProcessedFrames.isEmpty() && mProcessedFrames.peek() != null && mProcessedFrames.peek().getmHandMask() != null) {
if(Config.IS_DEBUG) System.out.println("Updating hand image view");
mHandImageView.setImage(Utils.cvMatToImage(mProcessedFrames.peekFirst().getmHandMask()));
}
}
#Override
public void run() {
while (!isInterrupted())
if (threadMessages.poll() == ThreadMessages.NEW_PROCESSED_FRAME_ARRIVED)
updateImageViews();
}
}
}
public class BufferLinkedList<E> extends LinkedList<E> {
private int counter = 0;
private int sizeLimit = 48;
public BufferLinkedList(int sizeLimit) {
this.sizeLimit = sizeLimit;
}
#Override
public synchronized boolean offerFirst(E e) {
while(size() > sizeLimit) {
removeLast();
}
return super.offerFirst(e);
}
#Override
public synchronized E peekFirst() {
return super.peekFirst();
}
#Override
public synchronized E peekLast() {
return super.peekLast();
}
#Override
public synchronized E pollFirst() {
return super.pollFirst();
}
#Override
public synchronized E pollLast() {
return super.pollLast();
}
}
My problems:
The frames are not displayed smoothly. there are a irregular, 1-5 seconds breaks between methods updating imageviews are fired. However the MyHandDetectionThread's task runs preaty quickly. And sizes of message queues of Display Threads are increasing fast. Maybe this is because of some locks on lists storing the frames?
Question:
Is my solution correct? Are there some design patterns describing this scenario? Do you have some suggestions for improvement?
Edit:
I've added waiting and notifying in thread loops. Results are satisfying. The CPU cost is now ~30% comparing to ~80% before. Everything runs more stable and smoothier. However, I'm not familiar with waiting and notifying aproach. So let me know if you find something stupid in my code.
public class VideoManager {
private volatile BufferLinkedList<InputFrame> mInputFrames;
private volatile BufferLinkedList<ProcessedFrame> mProcessedFrames;
private static VideoManager mVideoManagerInstance = new VideoManager();
private Timer captureTimer;
private MyVideoCaptureThread myVideoCaptureThread;
private MyFrameDisplayThread myFrameDisplayThread;
private MyHandDetectionThread myHandDetectionThread;
private MyGestureRecogitionThread myGestureRecogitionThread;
private MySkinDisplayThread mySkinDisplayThread;
private final static int THREAD_MESSAGES_LIMIT = 10000;
private final static int TIMER_INTERVAL = 1000 / Config.fps;
private final static int WAITING_TIMEOUT = 2000;
private enum ThreadMessages {
PROCESS_INPUT_FRAME,
NEW_INPUT_FRAME,
NEW_PROCESSED_FRAME_ARRIVED,
GET_NEW_FRAME
}
public static VideoManager getInstance() {
if (mVideoManagerInstance == null) {
mVideoManagerInstance = new VideoManager();
}
return mVideoManagerInstance;
}
// not visible constructor - for singleton purposes
private VideoManager() {
mInputFrames = new BufferLinkedList<>(Config.inputFramesListLimit);
mProcessedFrames = new BufferLinkedList<>(Config.inputFramesListLimit);
}
public void startDetectionAndRecognition(ImageView camIV, ImageView handIV) {
mInputFrames = new BufferLinkedList<>(Config.inputFramesListLimit);
mProcessedFrames = new BufferLinkedList<>(Config.inputFramesListLimit);
captureTimer = new Timer();
myFrameDisplayThread = new MyFrameDisplayThread(camIV);
myVideoCaptureThread = new MyVideoCaptureThread();
myHandDetectionThread = new MyHandDetectionThread();
myGestureRecogitionThread = new MyGestureRecogitionThread();
mySkinDisplayThread = new MySkinDisplayThread(handIV);
myVideoCaptureThread.start();
captureTimer.schedule(new TimerTask() {
public void run() {
if (myVideoCaptureThread != null && myVideoCaptureThread.threadMessages != null) {
myVideoCaptureThread.threadMessages.offer(ThreadMessages.GET_NEW_FRAME);
System.out.println("Timer get frame request sent");
myVideoCaptureThread.wakeUp();
}
}
}, 0, TIMER_INTERVAL);
myFrameDisplayThread.start();
mySkinDisplayThread.start();
myHandDetectionThread.start();
myGestureRecogitionThread.start();
}
public void stop() {
captureTimer.cancel();
myVideoCaptureThread.interrupt();
myHandDetectionThread.interrupt();
mySkinDisplayThread.interrupt();
myFrameDisplayThread.interrupt();
myGestureRecogitionThread.interrupt();
mInputFrames.removeAll(mInputFrames);
mProcessedFrames.removeAll(mProcessedFrames);
}
////////////////////////
// Lock class
////////////////////////
private static final class Lock {}
////////////////////////
// Thread clases
////////////////////////
private class MyVideoCaptureThread extends Thread {
volatile LinkedBlockingQueue<ThreadMessages> threadMessages = new LinkedBlockingQueue<>(THREAD_MESSAGES_LIMIT);
WebCamVideoCapture vc = new WebCamVideoCapture();
Lock lock = new Lock();
#Override
public void run() {
synchronized (lock) {
while (!isInterrupted()) {
if (threadMessages.poll() != ThreadMessages.GET_NEW_FRAME) {
try {
lock.wait(WAITING_TIMEOUT);
System.out.println("WideoCaptureThread waiting");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Mat mat = vc.getNextMatFrame();
System.out.println("getting next frame from webcam");
if (mat != null && mInputFrames != null) {
mInputFrames.offerFirst(new InputFrame(vc.getNextMatFrame()));
if (myHandDetectionThread != null && myHandDetectionThread.threadMessages != null) {
myHandDetectionThread.wakeUp();
myHandDetectionThread.threadMessages.offer(ThreadMessages.PROCESS_INPUT_FRAME);
}
if (myFrameDisplayThread != null && myFrameDisplayThread.threadMessages != null) {
myFrameDisplayThread.wakeUp();
myFrameDisplayThread.threadMessages.offer(ThreadMessages.NEW_INPUT_FRAME);
}
}
}
}
}
public void wakeUp() {
synchronized (lock) {
lock.notifyAll();
System.out.println("Waking up WideoCapture");
}
}
#Override
public void interrupt() {
vc.close();
super.interrupt();
}
}
private class MyFrameDisplayThread extends Thread {
volatile LinkedBlockingQueue<ThreadMessages> threadMessages = new LinkedBlockingQueue<>(THREAD_MESSAGES_LIMIT);
Lock lock = new Lock();
ImageView mCamImageView;
public MyFrameDisplayThread(ImageView mImageView) {
this.mCamImageView = mImageView;
}
private void updateImageViews() {
if (shouldUpdateCamImageView() && mInputFrames != null && !mInputFrames.isEmpty() && mInputFrames.peek() != null && mInputFrames.peek().getFrame() != null) {
System.out.println("Updating frame image view");
mCamImageView.setImage(Utils.cvMatToImage(mInputFrames.peekFirst().getFrame()));
threadMessages.poll();
}
}
#Override
public void run() {
synchronized (lock) {
while (!isInterrupted()) {
if (threadMessages.peek() != ThreadMessages.NEW_INPUT_FRAME) {
try {
lock.wait(WAITING_TIMEOUT);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
updateImageViews();
}
}
}
public void wakeUp() {
synchronized (lock) {
lock.notifyAll();
System.out.println("Waking up FrameDisplay");
}
}
private boolean shouldUpdateCamImageView() {
if (!Config.CAPTURE_PREVIEW_MODE) return false;
return true;
}
}
private class MySkinDisplayThread extends Thread {
volatile LinkedBlockingQueue<ThreadMessages> threadMessages = new LinkedBlockingQueue<>(THREAD_MESSAGES_LIMIT);
ImageView mHandImageView;
Object lock = new Lock();
public MySkinDisplayThread(ImageView mHandImageView) {
this.mHandImageView = mHandImageView;
}
private synchronized void updateHandImageView() {
if (shouldUpdateHandImageView() && mProcessedFrames != null && !mProcessedFrames.isEmpty() && mProcessedFrames.peek() != null && mProcessedFrames.peek().getmHandMask() != null) {
System.out.println("Updating skin image view");
mHandImageView.setImage(Utils.cvMatToImage(mProcessedFrames.peekFirst().getmHandMask()));
threadMessages.poll();
}
}
#Override
public void run() {
synchronized (lock) {
while (!isInterrupted()) {
if (threadMessages.peek() != ThreadMessages.NEW_PROCESSED_FRAME_ARRIVED) {
try {
lock.wait(WAITING_TIMEOUT);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
updateHandImageView();
}
}
}
private boolean shouldUpdateHandImageView() {
if (!Config.SKIN_MASK_PREVIEW_MODE) return false;
return true;
// long now = System.currentTimeMillis();
// boolean should = now - lastUpdatedHandImageViewMillis > TIMER_INTERVAL;
// lastUpdatedHandImageViewMillis = now;
// return should;
}
public void wakeUp() {
synchronized (lock) {
lock.notifyAll();
System.out.println("Waking up FrameDisplay");
}
}
}
private class MyHandDetectionThread extends Thread {
volatile LinkedBlockingQueue<ThreadMessages> threadMessages = new LinkedBlockingQueue<>(THREAD_MESSAGES_LIMIT); //TODO if multiple threads, define it out of class
HandDetector hd = new HandDetector();
Object lock = new Lock();
#Override
public void run() {
synchronized (lock) {
while (!isInterrupted()) {
if (threadMessages.poll() != ThreadMessages.PROCESS_INPUT_FRAME) {
try {
lock.wait(WAITING_TIMEOUT);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (mInputFrames != null /*&& mInputFrames.size() > 0 && mInputFrames.peek() != null && !mInputFrames.peek().getIsProcessed()*/) {
System.out.println("Detecting hand...");
// Mat handMask = hd.detectHand(mInputFrames.peek());
// int[][] fingerCoordinates = new int[5][2];
// int[] convDefects = new int[5];
// int[] handCenterCoordinates = new int[2];
mProcessedFrames.offerFirst(new ProcessedFrame(hd.detectHand(mInputFrames.peek()), null, null, null));
if (myGestureRecogitionThread != null && myGestureRecogitionThread.threadMessages != null) {
myGestureRecogitionThread.threadMessages.offer(ThreadMessages.NEW_PROCESSED_FRAME_ARRIVED);
mySkinDisplayThread.wakeUp();
}
if (mySkinDisplayThread != null && mySkinDisplayThread.threadMessages != null) {
mySkinDisplayThread.threadMessages.offer(ThreadMessages.NEW_PROCESSED_FRAME_ARRIVED);
mySkinDisplayThread.wakeUp();
}
}
}
}
}
public void wakeUp() {
synchronized (lock) {
lock.notifyAll();
System.out.println("Waking up hand Detection");
}
}
}
private class MyGestureRecogitionThread extends Thread {
volatile LinkedBlockingQueue<ThreadMessages> threadMessages = new LinkedBlockingQueue<>(THREAD_MESSAGES_LIMIT);
GestureRecognizer r = new GestureRecognizer();
Lock lock = new Lock();
#Override
public void run() {
synchronized (lock) {
while (!isInterrupted()) {
if (threadMessages.poll() != ThreadMessages.NEW_PROCESSED_FRAME_ARRIVED) {
try {
lock.wait(WAITING_TIMEOUT);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
r.lookForGestures(mProcessedFrames);
}
}
}
}
public void wakeUp() {
synchronized (lock) {
lock.notifyAll();
System.out.println("Waking up hand Detection");
}
}
}
}
Both threads seem to use polling in their run() method; i.e. they continuously loop around a statement checking a boolean condition. This can be bad for CPU usage, because a single thread can lock up the CPU without giving any cycles to other threads; it can end up hogging the CPU, even though it's not doing anything too useful; just failing some boolean condition.
You should use an asynchronous method for communicating with Threads; rather than using a polling mechanism, you should put threads to sleep when they are not required to do any processing, and wake them up once they're needed. This allows threads to yield the CPU, meaning that they willingly relinquish their active context so that other threads can execute.