I want to set up a repeating alarm which sends on its first trigger with Bundles the string "test".
Is it possible on the next trigger to have another string like "test2" ?
Alarm manager:
// Enable the scheduled alarm to send notifications
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent alarmIntent = new Intent(this, Service.class);
Bundle extras = new Bundle();
extras.putString("test", "test");
alarmIntent.putExtras(extras);
PendingIntent alarmPendingIntent = PendingIntent.getService(this, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
// Set the alarm in 30 minutes and repeat it every 30 minutes.
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + 1000,
1000,
alarmPendingIntent);
IntentService:
public class Service extends IntentService {
public Service() {
super("service");
}
#Override
protected void onHandleIntent(Intent intent) {
String test = intent.getStringExtra("test");
// update the bundle data
Log.i("result", test);
// how to update here the string to "test2" ?
}
}
I want on the second trigger the value of String test to be test2.
But I always get the original value from the first trigger.
You will need separate alarms and separate PendingIntents. You can either set them up at the same time, or set an alarm that does not repeat and have the service set the following alarm whenever it runs.
Note that if you set the two alarms at the same time, you need something other than the extras to be different because Android does not compare intent extras when it checks for an existing PendingIntent for the given Intent. One way to do this is like so:
Intent alarmIntent = new Intent(this, Service.class);
...
alarmIntent.putExtras(extras);
alarmIntent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
Related
My code
public class BackgroundIntentService extends IntentService {
public BackgroundIntentService() {
super("BackgroundIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
scheduleNextUpdate();
Log.w("Blabla", "asldad111");
Log.w("Blabla", "asldad");
Log.w("Blabla", "asldad");
Log.w("Blabla", "asldad555");
}
private void scheduleNextUpdate() {
Intent intent = new Intent(this, this.getClass());
PendingIntent pendingIntent =
PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// The update frequency should often be user configurable. This is not.
AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarm.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 5000, pendingIntent);
}
}
To start the service
Intent serviceIntent = new Intent(this, BackgroundIntentService.class);
startService(serviceIntent);
In the MainActivity.
The problem is that I can see in the logcat those logs spamming, not every 5 seconds but twice a second or more.
Where I'm wrong?
The flag you are using PendingIntent.FLAG_UPDATE_CURRENT in
PendingIntent pendingIntent =
PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
should be PendingIntent.FLAG_CANCEL_CURRENT
as quoted on the developer docs
FLAG_CANCEL_CURRENT - Flag indicating that if the described PendingIntent already exists, the current one should be canceled before generating a new one.
FLAG_UPDATE_CURRENT - Flag indicating that if the described PendingIntent already exists, then keep it but replace its extra data with what is in this new Intent.
so in your case the current pending intent exists and the new one updates it and fires at the exact moment it is updating the existing pending since you have defined the trigger time to be at System.currentTimeMillis()
So what is happening is that the current Pending intent is firing up before the new pending intent updates it ..and once it does that works as per the alarm logic , after the 5000ms interval the pending intent is fired. So there is a race condition here with interleaved alarm triggers and updates via the pending intents.
I am using Android AlarmManger to schedule an IntentService to run after small intervals.
Here is my AlarmManger:
static void setNextSchedule(Context ctx, long interval){
AlarmManager manager = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
Intent nIntent = new Intent(ctx, DService.class);
PendingIntent pIntent = PendingIntent.getActivity(ctx, 1, nIntent, PendingIntent.FLAG_UPDATE_CURRENT);
manager.setRepeating(AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + interval, interval, pIntent);
Log.i(ScheduleAlarm.class.getSimpleName(), "Next alarm set");
}
And my IntentService:
#Override
protected void onHandleIntent(Intent intent) {
Log.i(DService.class.getSimpleName(), "Service starting");
ScheduleAlarm.setNextSchedule(getApplicationContext(), 15000);
}
Currently it just holds code for testing. Now this code will run successfully with no problems once the app starts but after 15 seconds it will state that the service is starting in the logcat but the code won't execute.
How to get the intenet service the execute the code inside the onHandleIntent each time it runs?
Thanks
When using a PendingIntent, the factory method needs to match the type of component that your Intent is for:
If your Intent points to an activity, use getActivity()
If your Intent points to a service, use getService()
If your Intent points to a BroadcastReceiver, use getBroadcast()
If your Intent points to none of the above, go directly to jail, do not pass Go!, do not collect $200 :-)
I am developing an android app which shows a notification every 12 hour if the time is saved in the database. So everytime a data is entered or edited in the database ,I cancel the current alarmmanager and start a fresh new one so that I dont miss one. Also on reboot I have called the alarmmanager. On the broadcast receiver, the database is checked for entry and if found a notification is set and the app is opened automatically.
So when I test the app by changing the date manually,the app works as expected.Also on reboot the app works.But if I keep the app idle for nearly 14 hours,the notification is not set ,but if I open the app and suspend it the notification is set after that.
This is how I call the alarmmanager.
Intent alarmintent = new Intent(context, package.Alarm_Manager.class);
alarmintent.putExtra("note","Notify");
sender = PendingIntent.getBroadcast(context , 0 , alarmintent , PendingIntent.FLAG_CANCEL_CURRENT | Intent.FILL_IN_DATA);
alarm_manger = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
alarm_manger.cancel(sender);
Calendar cal = Calendar.getInstance();
long now = cal.getTimeInMillis();
alarmintent = new Intent(context, package.Alarm_Manager.class);
alarmintent.putExtra("note","Notification");
sender = PendingIntent.getBroadcast(context , 0 , alarmintent , PendingIntent.FLAG_CANCEL_CURRENT | Intent.FILL_IN_DATA);
alarm_manger = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarm_manger.setRepeating(AlarmManager.RTC_WAKEUP, now, AlarmManager.INTERVAL_HALF_DAY, sender);
This is the broadcast receiver
#Override
public void onReceive(Context context, Intent intent)
{
NotificationManager manger = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
Calendar cal = Calendar.getInstance();
date = (int)(cal.getTimeInMillis()/1000);
Notification notification = new Notification(R.drawable.vlcsnap_396460 , "Notify" , System.currentTimeMillis());
PendingIntent contentIntent = PendingIntent.getActivity(context, 0, intent, 0);
notification.setLatestEventInfo(context, "App", "Notify" , contentIntent);
notification.flags = Notification.FLAG_INSISTENT;
manger.notify( 0 , notification);
}
You don't need to call alarm_manager.cancel(sender); if you set the PendingIntent.FLAG_CANCEL_CURRENT.
Your call to
alarm_manger.setRepeating(AlarmManager.RTC_WAKEUP, now, AlarmManager.INTERVAL_HALF_DAY, sender);
will trigger the alarm right away, since the now is already passed when you set the alarm.
I suggest you use
now + DateUtils.HOUR_IN_MILLIS / 2
for the triggerAtMillis parameter
Did you tried to schedule it for smaller interval? Does it get triggered ?
After having seen your Alarm_Manager code, I think it is illegal to do this in your BroadcastReceiver object directly. Quote:
If this BroadcastReceiver was launched through a tag, then the object is no longer alive after returning from this function.
I believe there is no other way than to create a Service which is informed by your BroadcastReceiver, and make sure that the Service calls setLatestEventInfo() with itself (this) as the Context.
The reason why your asynchronous Broadcast fails while it works when your app is running is probably that the Context provided to the BroadcastReceiver lives only for the duration of the call to the BroadcastReceiver when your app does not run. So the Notification service, which only runs after your BroadcastReceiver has died along with the temporary context, is missing a valid context.
When your app runs, the Broadcast probably comes with your Activity or Application object as Context, and this is still vaild when the Notification manager runs.
Hope this helps.
Update: An `IntentService`` will do. You don't want a full time Service for that.
Update 2: Some snippets.
<service android:name=".MyIntentService" android:label="#string/my_intent_service_name" />
public final class MyIntentService extends IntentService {
public MyIntentService() {
super("service name");
// set any properties you need
}
#Override
public void onCreate() {
super.onCreate();
// do init, e.g. get reference to notification service
}
#Override
protected void onHandleIntent(Intent intent) {
// handle the intent
// ...especially:
notification.setLatestEventInfo(this, "App", "Notify" , contentIntent);
// ...
}
}
public final class MyAlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
context.startService(new Intent(context, MyIntentService.class));
}
}
I'm building an alarm application. I have successfully implemented basic alarm functions.
Calendar calendar = Calendar.getInstance();
calendar.set(calendar.HOUR_OF_DAY, sHour);
calendar.set(calendar.MINUTE, sMin);
calendar.set(calendar.SECOND, 0);
calendar.set(calendar.MILLISECOND, 0);
long sdl = calendar.getTimeInMillis();
Intent intent = new Intent(AlarmList.this, AlarmReceiver.class);
PendingIntent sender = PendingIntent.getBroadcast(AlarmList.this, 0, intent,PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager ALARM1 = (AlarmManager)getSystemService(ALARM_SERVICE);
ALARM1.set(AlarmManager.RTC_WAKEUP, sdl, sender);
In my application, user can select days (sunday,monday...) to repeat the alarm weekly.
I'm trying to create multiple alarms to repeat weekly but don't know how to do it.
Can I create it using (repeat) interval or should I create multiple alarm managers?
You need to use different Broadcast id's for the pending intents. Something like
this:
Intent intent = new Intent(load.this, AlarmReceiver.class);
final int id = (int) System.currentTimeMillis();
PendingIntent appIntent = PendingIntent.getBroadcast(this, id, intent, PendingIntent.FLAG_ONE_SHOT);
Using the system time should be a unique identifier for every pending
intent you fire.
From the docs:
If there is already an alarm for this Intent scheduled (with the
equality of two intents being defined by filterEquals(Intent), then
it will be removed and replaced by this one
Multiple AlarmManagers would not resolve your issue. If they have multiple different alarms (different times and different days), then you would need to set the alarm within the BroadcastReceiver every time you fire off a previous alarm.
You would also need to hold RECEIVE_BOOT_COMPLETED and have a BroadcastReceiver to receive the boot so that if the phone is rebooted you can re-schedule your alarms.
To set multiple alarms you need to define your Intent each time so that it is distinguishable from the others. The easiest way I find to do this is to set the data field of your Intent something as follows:
// give your alarm an id and save it somewhere
// in case you want to cancel it in future
String myAlarmId = ...;
// create your Intent
Intent intent = new Intent(AlarmList.this, AlarmReceiver.class);
intent.setData(Uri.parse("myalarms://" + myAlarmId));
...
The rest of your code #Hassy31 is fine as is and remains unchanged.
Note that the requestCode parameter in the PendingIntent.getBroadcast() method (as suggested by #parag) is unused according to the documentation so this ain't the right way to go about it.
set Broadcast id for pendingIntent
for (int id = 0; id < 3; id++) {
// Define pendingintent
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, id,ntent, 0);
// set() schedules an alarm
alarmManager.set(AlarmManager.RTC_WAKEUP, alertTime, pendingIntent);
}
Set multiple alarms using android alarm manager
//RC_ARRAY is store all the code that generate when alarm is set
private lateinit var RC_ARRAY:ArrayList<Int>
//tick is just hold the request when new alarm set
private var tick :Int=0
//setAlarm method set alarm
fun setAlarm(c: Calendar, context: Context) {
val manager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
//when alarm store set the request assign to tick variable
tick = System.currentTimeMillis().toInt()
//Add all the alarm Request into RC_ARRAY for just cancel the alarm
RC_ARRAY.add(tick)
//Notification Broadcast intent
val intentAlarm = Intent(context, AlaramFireReceiver::class.java).let {
PendingIntent.getBroadcast(context, tick, it, PendingIntent.FLAG_ONE_SHOT)
}
//alarm fire next day if this condition is not statisfied
if (c.before(Calendar.getInstance())) {
c.add(Calendar.DATE, 1)
}
//set alarm
manager.setExact(AlarmManager.RTC_WAKEUP, c.timeInMillis, intentAlarm)
}
//remove specific alarm
private fun removeAlarm(context: Context) {
val manager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
//remove specific alarm according to alarm request code
for (i in RC_ARRAY){
val intentAlarm = Intent(context, AlaramFireReceiver::class.java).let {
PendingIntent.getBroadcast(context, i, it, 0)
}
//cancel alarm
manager.cancel(intentAlarm)
}
}
//delivers notification for alarm
class AlaramFireReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
//Notification ID
val channelid="channelId"
val manger=context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
//check for device only available for Oreo and above
if (Build.VERSION.SDK_INT >=Build.VERSION_CODES.O){
val channel= NotificationChannel(channelid,"alarm notification",NotificationManager.IMPORTANCE_HIGH)
channel.enableLights(true)
manger.createNotificationChannel(channel)
}
//build notification
val build=NotificationCompat.Builder(context,channelid)
.setSmallIcon(R.drawable.ic_access_time_black_24dp)
.setContentTitle("alarm")
.setContentTitle("time done")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setColor(Color.RED)
//Deliver notification
manger.notify(0,build.build())
}
}
What I do is something similar to how you move to the next element in a linked list. I keep in database a ReminderEntity that has all the days of the week the user enabled the Alarm to go off.
Then I schedule the first day only. When the first day triggers then in that moment I schedule the next day and so on.
Same thing if the user deletes the first coming Alarm before it happens. In this case I clear the removed day from the entity and schedule an alarm for the next one available.
use different requestCode for the pending intents and use .FLAG_MUTABLE for type of Flag
int requestCode = (int) System.currentTimeMillis();
Intent intent = new Intent(load.this, AlarmReceiver.class);
return PendingIntent.getBroadcast(this, requestCode , intent, PendingIntent.FLAG_MUTABLE);
I have pending intent that sets alarm and it takes parameters such as row id and time from a database. I want to cancel alarm so i would do that by sending another pending intent with the same info and then cancel(i want to cancel it from different file). I only allow one alarm to be set at anyone time because that's the way my app works, because there is only one alarm set from that pending intent is there anyway i can just do cancel all for that intent?
Since I believe in code samples to get the point across, see below:
/*
* An alarm can invoke a broadcast request
* starting at a specified time and at
* regular intervals.
*/
public void sendRepeatingAlarm()
{
Calendar cal = Utils.getTimeAfterInSecs(30);
String s = Utils.getDateTimeString(cal);
this.mReportTo.reportBack(tag, "Schdeduling Repeating alarm in 5 sec interval starting at: " + s);
//Get an intent to invoke TestReceiver class
Intent intent = new Intent(this, TestReceiver.class);
intent.putExtra("message", "Repeating Alarm");
PendingIntent pi = this.getDistinctPendingIntent(intent, 2);
// Schedule the alarm!
AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
am.setRepeating(AlarmManager.RTC_WAKEUP,
cal.getTimeInMillis(),
5*1000, //5 secs
pi);
}
protected PendingIntent getDistinctPendingIntent(Intent intent, int requestId)
{
PendingIntent pi =
PendingIntent.getBroadcast(
this, //context
requestId, //request id
intent, //intent to be delivered
0);
//pending intent flags
//PendingIntent.FLAG_ONE_SHOT);
return pi;
}
/*
* An alarm can be stopped by canceling the intent.
* You will need to have a copy of the intent
* to cancel it.
*
* The intent needs to have the same signature
* and request id.
*/
public void cancelRepeatingAlarm()
{
//Get an intent to invoke TestReceiver class
Intent intent = new Intent(this, TestReceiver.class);
//To cancel, extra is not necessary to be filled in
//intent.putExtra("message", "Repeating Alarm");
PendingIntent pi = this.getDistinctPendingIntent(intent, 2);
// Schedule the alarm!
AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
am.cancel(pi);
}
I have pending intent that sets alarm and it takes parameters such as row id and time from a database.
There are no "parameters" on a PendingIntent. I am going to interpret this as meaning "extras".
I want to cancel alarm so i would do that by sending another pending intent with the same info and then cancel(i want to cancel it from different file).
It's not "sending" but "creating". Otherwise, yes, this is correct.
i can just do cancel all for that intent?
There is only one alarm.
Extras do not matter in terms of alarm scheduling. If you have an Intent (I1) wrapped in a PendingIntent (PI1) and use that to schedule an alarm, and later if you create an Intent (I2) with the same component/action/data/type, wrap that in a PendingIntent (PI2) and cancel() the alarm, it will cancel the PI1 alarm. Similarly, if you use PI2 to schedule a new alarm, it will remove the old PI1 alarm.