0

My app gains points everyday at 0000 Hours in the midnight, So i want to set this time and when the time reaches, the set time i want my app to add points automatically even if the app is not opened.

I tried below code but it is not working

//creates a time for 0000am
    String newDayTime = "00:00:00";
    Date date = java.sql.Time.valueOf(newDayTime);
    Calendar newDayDate = Calendar.getInstance();
    newDayDate.setTime(date);
    if(((today.getTime().after(newDayDate.getTime())))){
        dataa.edit().putFloat("blToday", blToday(select.this) + 15f).apply();
    }

The problem with this code is, it does not add point when 12:00am reaches, and the app also adds point everytime i launch it, every time when i open the app it adds points, How can i correct my code?

Phantômaxx
  • 37,901
  • 21
  • 84
  • 115
tinoe
  • 216
  • 2
  • 11
  • please study alaram manager in android – Syed Qasim Ahmed Jun 09 '18 at 19:51
  • 1
    FYI, the troublesome old date-time classes such as `java.util.Date`, `java.util.Calendar`, and `java.text.SimpleDateFormat` are now legacy, supplanted by the [*java.time*](https://docs.oracle.com/javase/10/docs/api/java/time/package-summary.html) classes. Much of the *java.time* functionality is back-ported to Java 6 & Java 7 in the [***ThreeTen-Backport***](http://www.threeten.org/threetenbp/) project. Further adapted for earlier Android in the [***ThreeTenABP***](https://github.com/JakeWharton/ThreeTenABP) project. See [*How to use ThreeTenABP…*](http://stackoverflow.com/q/38922754/642706). – Basil Bourque Jun 09 '18 at 20:04
  • What do you mean by "gains points" ? – android developer Jun 21 '18 at 08:48

3 Answers3

1

I think you should use ScheduledExecutorService. It will execute the submitted task (Runnable/Callable) at the scheduled time and also the task can be repeated periodically.

Create a Task like below:

class UpdatePointWorker implements Runnable {
    @Override
    public void run() {
        // write your code here to update the ponints
        // dataa.edit().putFloat("blToday", blToday(select.this) + 15f).apply();
    }
}

Schedule the task like below:

    ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    Long midnight = LocalDateTime.now().until(LocalDate.now().plusDays(1).atStartOfDay(), ChronoUnit.MINUTES);
    Runnable task = new UpdatePointWorker();
    scheduler.scheduleAtFixedRate(task, midnight, 1440, TimeUnit.MINUTES);
Amit Bera
  • 7,075
  • 1
  • 19
  • 42
1

If you have a very time sensitive task you should use AlarmManager, which will execute at the specified time regardless of system conditions. If you can afford flexibility, you can/should use JobScheduler. However the key principle of JobScheduler is that it will decide when the best time to execute your job is. You can't guarantee a delay of {x} minutes.

Example of an alaram manger

Calendar sixCalendar = Calendar.getInstance();
                    //set the time to 6AM
                    sixCalendar.set(Calendar.HOUR_OF_DAY, 6);
                    sixCalendar.set(Calendar.MINUTE, 0);
                    sixCalendar.set(Calendar.SECOND, 0);
    Intent intent = new Intent(this, AutoStartUp.class);   
     PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0,  intent, PendingIntent.FLAG_UPDATE_CURRENT);
     alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, sixCalendar.getTimeInMillis(),AlarmManager.INTERVAL_DAY, pendingIntent); 

Here is your broadcast recevier class

 public class AutoStartUp extends BroadcastReceiver{


    @Override
    public void onReceive(Context context, Intent intent) {
           specificMethod();// your method will be call here

    }
    }

Don't forget to register it mainfest

 <receiver android:name=".AutoStartUp"
                          android:enabled="true"
                        > 
                </receiver>
Syed Qasim Ahmed
  • 1,352
  • 13
  • 24
1

The Answer by Bera is close to correct, but chooses not quite the right java.time classes & methods.

ZonedDateTime & Instant, not LocalDateTime

It incorrectly uses the LocalDateTime class which lacks any concept of time zone or offset-from-UTC. So a LocalDateTime does not represent a specific moment on the timeline.

Instead, use ZonedDateTime to represent a specific moment with the wall-clock time used by the people of a particular region (a time zone).

A time zone is crucial in determining a date and a time-of-day. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec.

If no time zone is specified, the JVM implicitly applies its current default time zone. That default may change at any moment during runtime(!), so your results may vary. Better to specify your desired/expected time zone explicitly as an argument.

Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).

ZoneId z = ZoneId.of( "America/Montreal" ) ;  
ZonedDateTime now = ZonedDateTime.now( z ) ;

If you want to use the JVM’s current default time zone, ask for it and pass as an argument. If omitted, the JVM’s current default is applied implicitly. Better to be explicit, as the default may be changed at any moment during runtime by any code in any thread of any app within the JVM.

ZoneId z = ZoneId.systemDefault() ;  // Get JVM’s current default time zone.

Extract the date-only portion, without a time-of-day and without a time zone. Any java.time class named “Local…” means it is “unzoned”, without any concept of zone or offset.

LocalDate ld = now.toLocalDate();  // Extract date-only without time-of-day and without offset-from-UTC.

Avoid thinking/talking about “midnight” as that becomes a slippery concept. Instead, focus on “the first moment of the day”. So we need the first moment of tomorrow.

Determine “tomorrow”.

LocalDate tomorrow = ld.plusDays( 1 ) ;

Let java.time determine the first moment. Do not assume that will be the time-of-day 00:00:00. Anomalies such as Daylight Saving Time (DST) mean the day may start at a time such as 01:00:00. Specify a time zone, of course, to handle any anomalies affecting the people of this region, this time zone.

ZonedDateTime tomorrowStart = tomorrow.atStartOfDay( z ) ;

Calculate the span of time from now until then, the start of tomorrow. The Duration class represents a span of time unattached to the timeline.

Duration d = Duration.between( now , tomorrowStart ) ;

By the way, it may help your understanding to see those values in UTC. As a programmer (or sysadmin), you should be thinking in UTC. Forget about your own parochial time zone while on the job.

The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).

Instant instantNow = now.toInstant() ;
Instant instantTomorrowStart = tomorrowStart.toInstant() ;
Duration d = Duration.between( instantNow , instantTomorrowStart ) ;

That pair of Instant objects represent the very same moments as the pair of ZonedDateTime objects. Same points on the timeline, but different wall-clock time. So the resulting Duration calculation is the same.

ScheduledExecutorService::schedule, not scheduleAtFixedRate

The other Answer is correct in suggesting ScheduledExecutorService to schedule a task to be run at a certain time.

But the other Answer calls scheduleAtFixedRate which repeatedly executes the same task (a Runnable or Callable). But the Question specifies the requirement that the task be run at the first moment of the day, everyday. That time of day may change on various dates, because of anomalies such as Daylight Saving Time (DST) or other changes to the offset-from-UTC used by the particular time zone. These changes happen surprisingly often as politicians around the world show a proclivity to making these changes. So if you really mean to check the first moment of the day, then you need to recalculate the waiting duration each and every time your run the task.

So you should ask your executor service to run the task only once. And at the end of that task, schedule another run for the following day.

Set up your executor service, and save a reference to it somewhere in your app. The Executors class provides many such services. Get a count of either SECONDS or NANOS from generic Duration::get method. Using nanoseconds is a bit silly as an executor is not guaranteed to run with such precision. There is a always a fudge-factor involved, as the load on your CPU’s cores, your host OS’ multi-tasking scheduling, your JVM’s threading, all may impact when your task actually gets run. So I use seconds here.

By the way, a bit of greybeard advice… Midnight is the witching hour on computers. This is when many OSes and apps engage in clean-up and roll-over activities such as making fresh log files. Some programmers and sysadmins choose to run a little while after midnight, or later in the middle of the night such as 2 or 3 AM.

ScheduledExecutorService executorService = Executors.newSingleThreadExecutor() ;

Calculate a delay until the first execution.

long countUntilNextTaskRun = d.getSeconds() ; 

Schedule the task by calling ScheduledExecutorService.schedule and passing the amount of time to wait.

executorService.schedule( myRunnableOrCallable , countUntilNextTaskRun , unit ) ;

You may want to capture the ScheduledFuture for later use.

ScheduledFuture<?> scheduledFuture = executorService.schedule( myRunnableOrCallable , countUntilNextTaskRun , TimeUnit.SECONDS ) ;

This task will run only once. After that task does its main job have it also schedule the next run by using the same code seen above. Calculate the time until the next start-of-day in that particular time zone.

Lifecycle

The executor service lives in memory of your app. This has two important implications:

  • Be absolutely sure to shutdown the executor service. Otherwise, the thread pool and scheduled task may remain in memory even after your app has closed (depending on the implementation of your JVM and host OS). So your app should always detect when it is closing/exiting, and shutdown the executor service.
  • The executor service is not persisted after your app closes/exits. So when your app launches, your initialization/startup code must re-establish a fresh executor service.

If your business requirements ask that you catch-up on missed days of task-runs, then you will also need to program your task to record in some persistent manner the date-time (Instant) when it last ran. Then you calculate how many days you missed using the same kind of classes and methods discussed above.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • May you make it a code in one class form I am not good at coding so I get lost where exactly to put the code, onCreate() or where? – tinoe Jun 09 '18 at 20:41
  • From this, where do I put my method to add points at midnight? – tinoe Jun 09 '18 at 20:54
  • 1
    @tinoe Sorry, but that’s your job, not mine. If you need more help, search Stack Overflow. You will find *much* coverage of both Android and of the Java executors. Indeed your whole Question could probably be closed as a duplicate. – Basil Bourque Jun 09 '18 at 21:03