1

I have searched a lot of places but couldn't find a solution. I'm new to Android/Java programming and I'm trying to make an app that saves a counter in SQLite database from SharedPreferences daily (exact time) and reset it to 0 (like a step counter).

I'm using AlarmManager and BroadcastReceiver.
I'm setting the alarm using "setExactAndAllowWhileIdle" in one of the activities for first time, then onReceive() of BroadcastReceiver I'm calling a restartAlarm() method to reschedule the alarm.

The problem
Alarm triggers correctly for first time at specific time (e.g. 14:00:00).
Also rescheduled and triggered correctly after 3 minutes (14:03:00), but only once.

The problem I'm facing is that after rescheduled alarm triggers for first time after 3 minutes (from restartAlarm() method in BroadcastReceiver), then the alarm triggers every 5 seconds and not 3 minutes (I want daily repeating alarm but I'm setting alarm every 3 minutes only for testing results).

This is what I'm doing:

I'm setting permission and receiver in Manifest.xml file

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

<application
    android:name=".ApplicationClass"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">

    ....

    <receiver
        android:name=".MyAlarmReceiver"
        android:directBootAware="true"
        android:enabled="true"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
            <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
        </intent-filter>
    </receiver>

</application>

Starting the alarm in one of app's activities:

public class CompleteRegistered extends AppCompatActivity {

...

public static AlarmManager alarmManager;
public static PendingIntent pendingIntent;
private static final int GLASS_REQUEST_CODE = 101;

...


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_complete_registered);

    ...

    startAlarmManager();
}

private void startAlarmManager(){

    // Creating the pending intent to send to the BroadcastReceiver
    alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    Intent alarmIntent = new Intent(getApplicationContext(), MyAlarmReceiver.class);
    pendingIntent = PendingIntent.getBroadcast(this, GLASS_REQUEST_CODE, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);

    // Setting the specific time for the alarm manager to trigger the intent
    Calendar startTime = Calendar.getInstance();
    startTime.setTimeInMillis(System.currentTimeMillis());
    startTime.set(Calendar.HOUR_OF_DAY, 14);
    startTime.set(Calendar.MINUTE, 0);
    startTime.set(Calendar.SECOND, 0);


    // Starts the alarm manager
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

        alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC, startTime.getTimeInMillis(), pendingIntent);

    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {

        alarmManager.setExact(AlarmManager.RTC, startTime.getTimeInMillis(), pendingIntent);

    } else {

        alarmManager.set(AlarmManager.RTC, startTime.getTimeInMillis(), pendingIntent);
    }

}

Saving counter to SQLite database and rescheduling alarm in BroadcastReceiver:

public class MyAlarmReceiver extends BroadcastReceiver {

private static final String MyPREFERENCES = "MyPrefs" ;
private SharedPreferences sharedpreferences;

public static AlarmManager alarmManager;
public static PendingIntent pendingIntent;
public static Intent alarmIntent;
private static final int GLASS_REQUEST_CODE = 102;

// Database Helper
private DatabaseHelper db;

public void restartAlarm(Context context){

    // Creating the pending intent to send to the BroadcastReceiver
    alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    alarmIntent = new Intent(context, MyAlarmReceiver.class);
    pendingIntent = PendingIntent.getBroadcast(context, GLASS_REQUEST_CODE, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);

    // Setting the specific time for the alarm manager to trigger the intent
    Calendar startTime = Calendar.getInstance();
    startTime.setTimeInMillis(System.currentTimeMillis());
    startTime.set(Calendar.HOUR_OF_DAY, 14);
    startTime.set(Calendar.MINUTE, 0);
    startTime.set(Calendar.SECOND, 0);

    // Getting time
    Calendar now = Calendar.getInstance();

    if (now.after(startTime)){

        // startTime.add(Calendar.DATE, 1);
        startTime.add(Calendar.MINUTE, 3);
    }


    // Starts the alarm manager
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

        alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC, startTime.getTimeInMillis(), pendingIntent);

    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {

        alarmManager.setExact(AlarmManager.RTC, startTime.getTimeInMillis(), pendingIntent);

    } else {

        alarmManager.set(AlarmManager.RTC, startTime.getTimeInMillis(), pendingIntent);
    }

}


@Override
public void onReceive(Context context, Intent intent) {

    // When device reboots
    if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) {

        // Set the alarm.
        startAlarm(context);

    }
    else {
        sharedpreferences = context.getSharedPreferences(MyPREFERENCES, Activity.MODE_PRIVATE);
        final SharedPreferences.Editor editor = sharedpreferences.edit();

        // Get glasses from SharedPreferences
        int glassCounter = sharedpreferences.getInt("glasses", 0);

        // Create a WaterTracker
        WaterTracker waterTracker = new WaterTracker();
        waterTracker.setGlasses(glassCounter);

        // Store to SQLite database
        db = new DatabaseHelper(context);
        db.createWaterTracker(waterTracker);

        // Reset glasses from SharedPreferences
        editor.putInt("glasses", 0);
        editor.apply();

        // Set the alarm again.
        restartAlarm(context);
    }
}

Also I have an ApplicationClass that extends Application:

public class ApplicationClass extends Application {

    ...

    public static AlarmManager alarmManager;
    public static PendingIntent pendingIntent;
    private static final int GLASS_REQUEST_CODE = 101;

    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    public void onCreate() {
        super.onCreate();

        ...

        ComponentName receiver = new ComponentName(getApplicationContext(), MyAlarmReceiver.class);
        PackageManager pm = getApplicationContext().getPackageManager();

        pm.setComponentEnabledSetting(receiver,
                PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                PackageManager.DONT_KILL_APP);

    }
}

The results I'm getting from this code are:
1. (counter value) at 14:00:00
2. (counter value) at 14:03:00
3. (counter value) at 14:03:05
4. (counter value) at 14:03:10
5. (counter value) at 14:03:15
6. (counter value) at 14:03:20
7. (counter value) at 14:03:25
8. (counter value) at 14:03:30
...
?. (counter value) at 14:05:25

Any help with this would be much appreciated!
Thank you in advance!

elgeo07
  • 11
  • 3
  • you want to repeat alarm every 5 min the setInexactRepeating() method you can use because above code is quite more complex for given task. – haresh Jan 02 '20 at 11:21
  • the problem is when you are testing inside if (now.after(startTime)) the hour of day is always past 14 and the alarm triggers as fast as possible. You should reset startTime to now + 3 minutes – Alex Aug 19 '20 at 16:40

1 Answers1

0

Try out this for repeating task of alarm manager.

private void repeateAlarm() {

    Calendar updateTime = Calendar.getInstance();

    updateTime.set(alendar.MINUTE, 3);

    Intent alarmIntent = new Intent(this, AlarmReceiver.class);
    PendingIntent recurringDownload = PendingIntent.getBroadcast(this, 0, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
    AlarmManager alarms = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    alarms.cancel(recurringDownload);
    alarms.setInexactRepeating(AlarmManager.RTC_WAKEUP, updateTime.getTimeInMillis(), 1000 * 60*3, recurringDownload); // it will run in every 3 min
}
haresh
  • 1,424
  • 2
  • 12
  • 18
  • I'm using **setExactAndAllowWhileIdle** because I want to repeat alarm at specific time. I think **setInexactRepeating** is not doing this. Although I will give a try. – elgeo07 Jan 02 '20 at 11:50
  • I'm still getting same error. Now alarm triggers almost every 1 minute. – elgeo07 Jan 03 '20 at 00:37