Countdown Timer Android App(Firebase Realtime database) [duplicate] - java

I have an app where one user hosts a game, and then other users can vote on questions from the host. From the moment the host posts the question, the players have 20 seconds to vote.
How can I show a countdown timer on all player's screens and keep them synchronized with the host?

Many developers get stuck on this problem because they try to synchronize the countdown itself across all users. This is hard to keep in sync, and error prone. There is a much simpler approach however, and I've used this in many projects.
All each client needs to show its countdown timer are three fairly static pieces of information:
The time that the question was posted, which is when the timer starts.
The amount of time they need to count from that moment.
The relative offset of the client to the central timer.
We're going to use the server time of the database for the first value, the second value is just coming from the host's code, and the relative offset is a value that Firebase provides for us.
The code samples below are written in JavaScript for the web, but the same approach (and quite similar code) and be applied in iOS, Android and most other Firebase SDKs that implement realtime listeners.
Let's first write the starting time, and interval to the database. Ignoring security rules and validation, this can be as simple as:
const database = firebase.database();
const ref = database.ref("countdown");
ref.set({
startAt: ServerValue.TIMESTAMP,
seconds: 20
});
When we execute the above code, it writes the current time to the database, and that this is a 20 second countdown. Since we're writing the time with ServerValue.TIMESTAMP, the database will write the time on the server, so there's no chance if it being affected by the local time (or offset) of the host.
Now let's see how the other user's read this data. As usual with Firebase, we'll use an on() listener, which means our code is actively listening for when the data gets written:
ref.on("value", (snapshot) => {
...
});
When this ref.on(... code executes, it immediately reads the current value from the database and runs the callback. But it then also keeps listening for changes to the database, and runs the code again when another write happens.
So let's assume we're getting called with a new data snapshot for a countdown that has just started. How can we show an accurate countdown timer on all screens?
We'll first get the values from the database with:
ref.on("value", (snapshot) => {
const seconds = snapshot.val().seconds;
const startAt = snapshot.val().startAt;
...
});
We also need to estimate how much time there is between our local client, and the time on the server. The Firebase SDK estimates this time when it first connects to the server, and we can read it from .info/serverTimeOffset in the client:
const serverTimeOffset = 0;
database.ref(".info/serverTimeOffset").on("value", (snapshot) => { serverTimeOffset = snapshot.val() });
ref.on("value", (snapshot) => {
const seconds = snapshot.val().seconds;
const startAt = snapshot.val().startAt;
});
In a well running system, the serverTimeOffset is a positive value indicating our latency to the server (in milliseconds). But it may also be a negative value, if our local clock has an offset. Either way, we can use this value to show a more accurate countdown timer.
Next up we'll start an interval timer, which gets calls every 100ms or so:
const serverTimeOffset = 0;
database.ref(".info/serverTimeOffset").on("value", (snapshot) => { serverTimeOffset = snapshot.val() });
ref.on("value", (snapshot) => {
const seconds = snapshot.val().seconds;
const startAt = snapshot.val().startAt;
const interval = setInterval(() => {
...
}, 100)
});
Then every timer our interval expires, we're going to calculate the time that is left:
const serverTimeOffset = 0;
database.ref(".info/serverTimeOffset").on("value", (snapshot) => { serverTimeOffset = snapshot.val() });
ref.on("value", (snapshot) => {
const seconds = snapshot.val().seconds;
const startAt = snapshot.val().startAt;
const interval = setInterval(() => {
const timeLeft = (seconds * 1000) - (Date.now() - startAt - serverTimeOffset);
...
}, 100)
});
And then finally we log the remaining time, in a reasonable format and stop the timer if it has expired:
const serverTimeOffset = 0;
database.ref(".info/serverTimeOffset").on("value", (snapshot) => { serverTimeOffset = snapshot.val() });
ref.on("value", (snapshot) => {
const seconds = snapshot.val().seconds;
const startAt = snapshot.val().startAt;
const interval = setInterval(() => {
const timeLeft = (seconds * 1000) - (Date.now() - startAt - serverTimeOffset);
if (timeLeft < 0) {
clearInterval(interval);
console.log("0.0 left)";
}
else {
console.log(`${Math.floor(timeLeft/1000)}.${timeLeft % 1000}`);
}
}, 100)
});
There's definitely some cleanup left to do in the above code, for example when a new countdown starts while one is still in progress, but the overall approach works well and scales easily to thousands of users.

Related

How to generate more than 1000 events / second in java using sleep time

I have a generator which generates events for Flink CEP, code for which is given below. Basically, I am using Thread.sleep() and I have read somewhere that java can't sleep less than 1 millisecond even we use System.nanoTime(). Code for the generator is
public class RR_interval_Gen extends RichParallelSourceFunction<RRIntervalStreamEvent> {
Integer InputRate ; // events/second
Integer Sleeptime ;
Integer NumberOfEvents;
public RR_interval_Gen(Integer inputRate, Integer numberOfEvents ) {
this.InputRate = inputRate;
Sleeptime = 1000 / InputRate;
NumberOfEvents = numberOfEvents;
}
#Override
public void run(SourceContext<RRIntervalStreamEvent> sourceContext) throws Exception {
long currentTime;
Random random = new Random();
int RRInterval;
int Sensor_id;
for(int i = 1 ; i <= NumberOfEvents ; i++) {
Sensor_id = 2;
currentTime = System.currentTimeMillis();
// int randomNum = rand.nextInt((max - min) + 1) + min;
RRInterval = 10 + random.nextInt((20-10)+ 1);
RRIntervalStreamEvent stream = new RRIntervalStreamEvent(Sensor_id,currentTime,RRInterval);
synchronized (sourceContext.getCheckpointLock())
{
sourceContext.collect(stream);
}
Thread.sleep(Sleeptime);
}
}
#Override
public void cancel() {
}
}
I will specify my requirement here in simple words.
I want generator class to generate events, let's say an ECG stream at 1200 Hz. This generator will accept parameters like input rate and total time for which we have to generate the stream.
So far so good, the issue is that I need to send more than 1000 events / second. How can I do this by using generator function which is generating values U[10,20]?
Also please let me know if I am using wrong way to generate x number of events / second in the above below.
Sleeptime = 1000 / InputRate;
Thanks in advance
The least sleep time in Windows systems is ~ 10 ms and in Linux and Macintosh is 1 millisecond as mentioned here.
The granularity of sleep is generally bound by the thread scheduler's
interrupt period. In Linux, this interrupt period is generally 1ms in
recent kernels. In Windows, the scheduler's interrupt period is
normally around 10 or 15 milliseconds
Through my research, I learned that using the nano time sleep in java will not help as the issue in at OS level. If you want to send data at arrival rate > 1000 in a controlled way, then it can be done using Real-Time Operating Systems (RTOS), as they can sleep for less then a millisecond. Now, I have come up with another way of doing it, but in this solution, the interarrival times will not be constantly distributed.
Let's say you want arrival rate of 3000 events/ second, then you can create a for loop which iterates 3 times to send data in each iteration and then sleep for 1ms. So for the 3 tuples, the interarrival time will be close to one another, but the issue will be solved. This may be a stupid solution but it works.
Please let me know if there is some better solution to this.

Java calculations that takes X amount of time

This is just a hypothetical question, but could be a way to get around an issue I have been having.
Imagine you want to be able to time a calculation function based not on the answer, but on the time it takes to calculating. So instead of finding out what a + b is, you wish to continue perform some calculation while time < x seconds.
Look at this pseudo code:
public static void performCalculationsForTime(int seconds)
{
// Get start time
int millisStart = System.currentTimeMillis();
// Perform calculation to find the 1000th digit of PI
// Check if the given amount of seconds have passed since millisStart
// If number of seconds have not passed, redo the 1000th PI digit calculation
// At this point the time has passed, return the function.
}
Now I know that I am horrible, despicable person for using precious CPU cycles to simple get time to pass, but what I am wondering is:
A) Is this possible and would JVM start complaining about non-responsiveness?
B) If it is possible, what calculations would be best to try to perform?
Update - Answer:
Based on the answers and comments, the answer seems to be that "Yes, this is possible. But only if it is not done in Android main UI thread, because the user's GUI will be become unresponsive and will throw an ANR after 5 seconds."
A) Is this possible and would JVM start complaining about non-responsiveness?
It is possible, and if you run it in the background, neither JVM nor Dalvik will complain.
B) If it is possible, what calculations would be best to try to perform?
If the objective is to just run any calculation for x seconds, just keep adding 1 to a sum until the required time has reached. Off the top of my head, something like:
public static void performCalculationsForTime(int seconds)
{
// Get start time
int secondsStart = System.currentTimeMillis()/1000;
int requiredEndTime = millisStart + seconds;
float sum = 0;
while(secondsStart != requiredEndTime) {
sum = sum + 0.1;
secondsStart = System.currentTimeMillis()/1000;
}
}
You can and JVM won't complain if your code is not part of some complex system that actually tracks thread execution time.
long startTime = System.currentTimeMillis();
while(System.currentTimeMillis() - startTime < 100000) {
// do something
}
Or even a for loop that checks time only every 1000 cycles.
for (int i = 0; ;i++) {
if (i % 1000 == 0 && System.currentTimeMillis() - startTime < 100000)
break;
// do something
}
As for your second question, the answer is probably calculating some value that can always be improved upon, like your PI digits example.

Handler.postDelayed function doesn't create time delays ? - Android

Below I have a Runnable "updater" ...and an OnClick function that uses Handler.PostDelayed function to run the runnable after a delay...
After a little editing, cutting of useless parts here are the functions:
(passtog = Toggle Button)
final Runnable updater = new Runnable() {
#Override
public void run() {
if (passTog.isChecked()) {
now = System.currentTimeMillis();
time = now - init;
if (time > 5000) {
Toast.makeText(getApplicationContext(), "WAKE UP !",
Toast.LENGTH_SHORT).show();
}
handler.postDelayed(this, 25);
}
}
};
passTog.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
init = System.currentTimeMillis();
flag = true;
handler.postDelayed(updater,
(new Random().nextInt(4000) + 3000));
}
});
}
Explaination
Basically, The user toggles the Toggle button. Now it's on: The runnable can run completely (Everything is in the if block).
If the user doesn't press the button again, and switches it off The app sends a Toast "Wake Up!" ..It runs and checks every 25 millisecs to update the time...
Pretty straightforward... Yet I'm having a problem.
Before the program actually gets to the runnable, I absolutely NEED there to be a minimum time delay of 3 seconds + Some Random value ... So it varies between 3 sec - 7 sec. It SHOULD vary between 3-7 , but it doesn't.
When I run it: The problem
I notice that the first time, it works great... I get atleast a 3 sec delay + a random value= Perfect
The second time, that is after the switch goes on ->off-> on : Now It acts like it doesn't see the +3000 ...and just the ~randInt(4000) function... So it may give 0 sec or it may give 4 sec delay...
In all my experience, I've never really come across this.. I've rewritten the entire code, My other apps use this function in exactly the same sytax and seem to do pretty great.. Why is this creating a problem ? Could the Toast's time possibly be causing a problem..
How to solve this ?
(I'm open to other methods, preferably quick to implement. I want a minimum 3 sec delay which I'm not getting for some reason... I need the UI to be responsive though So no thread sleeping.)
You probably should call Handler.removeCallbacksAndMessages(null) when the switch goes off.

Java Gradually Decrease Number Until Target Reached Over Time

I am struggling a bit with a concept on how to implement some timings.
Basically, I need to write some code that will effectively perform a ramp-up to fire requests to a server.
To explain further, I need to fire requests to a server over the course of a 15 minute ramp-up period. After 15 minutes, a rate of 3 requests per second should be made. At the beginning of the ramp up period, we can start with (say) 1 request every 3 seconds. How it reaches the 3 request per second rate doesn't matter, but it should not reach this rate until 15 minutes.
What I need help with is implementing this as a timer. I need a function that will return the amount of time to wait before sending the next request.
So I have a loop like so:
Send request
Wait x amount of time (where x is returned by a function)
This happens until 15 minutes is reached, whereby the function always returns a value of 0.3 seconds (to achieve 3 requests per second - assumption is that requests take 0 seconds to send, but that's ok...)
The values provided are:
- Total ramp up time.
- Requests per second at the end of the ramp up time.
- Requests per second at the start of the ramp up time.
Any help would be appreciated.
Since you are not too concerned about the exact way the rate speeds up, you could choose the following assumptions:
Rate will increase linearly with time
Some approximations and rounding of the rate is fine, as long as we don't slow down
You are starting at time=0, and going to time=15
At time=0, your rate is (say) 1 every 3 seconds. At time=15, your rate is 1 every 0.3333 seconds
The total change from 0 to 15 is (3 - 0.3333=) 2.77777
Divide this by 15, you get 0.1777777. What this means is: if your rate fell by 0.177777 every second, you could start at 3 and end up at 0.3333
This is shown on a linear graph like this:
So, if you have a method that know how long (in seconds) since the start (x), you can compute what your current rate should be.
double computeRate(double secondsSinceStart)
{
return 3 * (-0.177777 * Math.floor(secondsSinceStart));
}
That computation is the number of seconds you have to wait.
Using a similar principle, you can assume a non-linear curve, or tweak it in other ways.
/**
* start is the moment the first request is sent (in ms)
* end is the moment, in which the targetDelta should be reached (in ms)
* targetDelta is the targeted period between two requests (0.3)
* initDelta is the initial delta (1.0)
*/
private int getWaitingPeriod(long start, long end, double targetDelta, double initDelta) {
double timePassed = (double) (System.currentTimeMillis() - start);
double progress = timePassed / (double) (end - start);
if(progress >= 1) return (int) (targetDelta * 1000);
return (int) ((targetDelta - (targetDelta - initDelta) * progress) * 1000);
}
Not tested, but is this what you are searching for?
edit: whoops, forgot converting seconds to ms.. Now tested, example:
long start = System.currentTimeMillis();
while(System.currentTimeMillis() < start + 10000) { //testing with 10 seconds
int wait = getWaitingPeriod(start, start + 10000, 1, 0.3);
System.out.println("waiting " + wait + "ms");
try {
Thread.sleep(wait);
} catch(InterruptedException ex) {}
}
You can start by creating a class that handles sending the requests if that's not already the case. Something like (this is more Object Oriented) :
public class RequestSender {
double startTime;
// ramp up time is in minutes
double rampUpTime;
boolean firstRequest;
int requestPerSecBeforeTime;
int requestPerSecAfterTime;
RequestSender(double rampUpTime, int requestPerSecBeforeTime, int requestPerSecAfterTime){
this.rampUpTime = rampUpTime;
this.requestPerSecAfterTime = requestPerSecAfterTime;
this.requestPerSecBeforeTime = requestPerSecAfterTime;
firstRequest=true;
}
public void sendRequest(){
if (firstRequest){
startTime = System.currentTimeMillis();
firstRequest = false;
}
// do stuff to send requests
}
public double getWaitTime(){
if ((System.currentTimeMillis() - startTime)/60000 > rampUpTime){
return 1/requestPerSecAfterTime;
}
else {
return 1/requestPerSecBeforeTime;
}
}
}
Then you can use this object in your code :
RequestSender rs = new RequestSender(15, 1, 3);
rs.sendRequest();
Thread.wait(rs.getWaitTime());

Programming a delay in javafx script, is this possible?

Ok, so i have a problem with javafx, the netbeans 6.9.1 version that is (i know it's a bit old but i have to do this for a final assignement for school). So for the assignement i have to code a memory game (concentration). Now i want to program a delay so when i flip a card the function waits for like 1.5 seconds so that you can see the turned card. however, when i make a while loop to wait for the 1.5 second mark the program just freezes and the variable "time02" won't update. i then have to manually shut the program down via task manager because it just freezes. Here's a snippet of the code with the timeline and a small piece of the function. i also included the timeline for the clock which weirdly enough updates the variable "time01" just fine. I also put the code for delay in a comment.
clock = Timeline {
repeatCount: Timeline.INDEFINITE
keyFrames: KeyFrame {
time: 0.1s
action: function() {
updateclock();
}
}
}
function updateclock() {
time01 = time01 + 1;
text2.content = "Tijd: {time01 / 10}";
}
/*
delay = Timeline {
repeatCount: Timeline.INDEFINITE
keyFrames: KeyFrame {
time: 0.1s
action: function() {
updatedelay();
}
}
}
function updatedelay() {
time02 = time02 + 0.1;
}
*/
function control() {
if (counter == 2) {
/*
while (time02 < 1.2) {
delay.play();
}
delay.stop();
time02 = 0;
*/
..............................
Any type of help would be very much appreciated!
It's treading issue. You can't wait for something which is calculated on the same thread.
You can put code which flips card back into the Timeline to make JavaFX care about threading instead of you.
backflip : Timeline {
repeatCount: 1
keyFrames: KeyFrame {
time: 1.5s
action: function() {
// here is code which flips card back
}
}
}

Categories