Reputation: 43
I have a birthday app that needs to check local SharedPreferences data once a day to see if somebody's birthday is that day.
As of now, I am trying to do something supposedly simple: send a test notification every minute EVEN when I click the back button, swipe up to close the app, open another app, phone off (not shut down), etc. Plenty of other apps do this.
If I can get this about-every-minute reminder working, I can adapt it to show data from SharedPreferences, which isn't a big deal. There is one problem however:
ALARMMANAGER simply IS NOT working the way they say it should in the docs.
I use setInexactRepeating for my alarm manager and have that trigger a class called "ReminderAlarm", this then starts an Service that creates a notification channel and builds a test notification. The issue is that when I exit the app, no notifications will show at all. As soon as I open the app, however, they will. So for some reason AlarmManager is not firing unless the app is open. (I tried this with Log.d as well, it also only prints to the log if the app is open)
This is what sets the repeating alarm:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_enter);
ToggleButton toggle = (ToggleButton) findViewById(R.id.toggleButton);
toggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked) {
setAlarm();
} else {
cancelAlarm();
}
}
});
}
private void setAlarm() {
alarmManager = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, ReminderAlarm.class);
pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), 100, pendingIntent);
}
private void cancelAlarm() {
//alarmManager.cancel(pendingIntent);
}
This is the alarm receiver: (It is defined in the manifest as a receiver)
public class ReminderAlarm extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, ReminderBuilderService.class);
context.startService(i);
}
}
This is the reminder builder service: (It is defined in the manifest as a service)
public class ReminderBuilderService extends Service {
public final String CHANNEL_ID = "MainChannel";
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
createNotificationChannel();
buildNotification();
return super.onStartCommand(intent, flags, startId);
}
private void createNotificationChannel() {
String name = "Birthday Notifications";
String description = "Reminds you when your friends birthday approaches";
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
channel.setDescription(description);
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
private void buildNotification() {
Context context = getApplicationContext();
Intent openTap = new Intent(context, EnterActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, openTap, 0);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID);
builder.setSmallIcon(R.drawable.pix_cake);
builder.setContentTitle("TestNotification");
builder.setStyle(new NotificationCompat.BigTextStyle()
.bigText("TestText"));
builder.setPriority(NotificationCompat.PRIORITY_HIGH);
builder.setContentIntent(pendingIntent);
builder.setAutoCancel(true);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
notificationManager.notify(0, builder.build());
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
A Notification should show up around every minute (I know that setInexactRepeating does not allow for exact repeated timings) but it only shows WHEN THE APP IS OPEN
Finally, here is the Android Manifest xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myfirstapp">
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.SET_ALARM" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:allowBackup="true"
android:icon="@mipmap/main_icon"
android:label="@string/app_name"
android:roundIcon="@mipmap/main_icon_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<receiver android:name=".ReminderAlarm"/>
<activity android:name=".ListOfDaysActivity" />
<activity android:name=".MainActivity" />
<activity android:name=".EnterActivity"
android:launchMode="singleInstance">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".ReminderAlarm"/>
<service android:name=".ReminderBuilderService"/>
</application>
</manifest>
Is there anything I'm missing?
Upvotes: 0
Views: 1099
Reputation: 885
You could use JobScheduler
or AlarmManager
perhaps to achieve your desired results. But what you should be using is WorkManager
an abstraction of both depending on Android version.
WorkManager
is highly configurable and will allow you to create a PeriodicWorkRequest
or a OneTimeWorkRequest
these are guaranteed to succeed. PeriodicWorkRequest
will fire when you schedule the work, as well as when you have specified in the timer. It will execute in the background even if the app is closed or backgrounded. If you didn't want your task to execute immediately you can use a PWR(PeriodicWorkRequest) with a FlexInterval. See the docs below for more info.
For example, I created two PeriodicWorkRequests that refresh services and keeps the user logged in always by renewing their token. When the user authenticates the PeriodicWorkRequest
is created. In my case, I didn't need it to fire right away as they have just received and cached this information so I utilized the FlexInterval. When the app is backgrounded or closed, the workers continue to refresh services every 12 hours and refresh the token every 6. It works like a charm.
Here is an example:
Build Work:
override fun beginWork() {
val periodicWorkRequest = PeriodicWorkRequest.Builder(
MyWorker::class.java,
REPEAT_INTERVAL, TimeUnit.MINUTES, // How often work should repeat
// Flex not required.
FLEX_INTERVAL, TimeUnit.MINUTES) // Limits execution into a time window
.setConstraints(
Constraints.Builder().setRequiredNetworkType(
NetworkType.CONNECTED).build())
.addTag(MY_WORKER_TAG)
.build()
WorkManager.getInstance().enqueueUniquePeriodicWork(
MY_UNIQUE_WORK,
ExistingPeriodicWorkPolicy.KEEP,
periodicLoginRequest)
Worker:
class MyWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
override fun doWork(): Result {
// DO WORK HERE
Result.success()
} else {
// HANDLE FAILURE HERE
Result.failure()
}
The above is a simple implementation, but it should give you the general idea.
Upvotes: 2