I'm trying to create an Alarm that will fix lost connections to Google Cloud Messaging that occur due to the heartbeat bug, found here How to avoid delay in Android GCM messages / change heartbeat
However, my alarm's onReceive class which I have being set to be called every 1 minute for testing is never being called. I've looked in several of the other questions pertaining to this topic, and all of them concentrate on spelling and declaring the receiver in the manifest, which I've checked several times.
Here is all the relevant code:
MainActivity.java
//Alarm created here
GCMHeartbeatAlarm gcmAlarm = new GCMHeartbeatAlarm();
gcmAlarm.setAlarm(this);
GCMHeartbeatAlarm.java
public class GCMHeartbeatAlarm extends BroadcastReceiver {
private AlarmManager alarmMgr;
private PendingIntent alarmIntent;
#Override
public void onReceive(Context context, Intent intent) {
//The part which is supposedly going to fix the GCM connection dropped bug, needs to be called every 5 mins or so via alarm to keep
//GCM connection open
//Commented out for now
// context.sendBroadcast(new Intent("com.google.android.intent.action.GTALK_HEARTBEAT"));
// context.sendBroadcast(new Intent("com.google.android.intent.action.MCS_HEARTBEAT"));
Log.i("GCMHeartbeat", "GCM Heartbeat Sent!");
}
public void setAlarm(Context context) {
alarmMgr=(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, GCMHeartbeatAlarm.class);
alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
//Repeat every 1 minute
alarmMgr.setRepeating(AlarmManager.ELAPSED_REALTIME, System.currentTimeMillis(), 1000 * 60 * 1 , alarmIntent);
Log.i("GCMHeartbeat", "Alarm set!");
}
}
AndroidManifest.xml
<!-- GCM Heartbeat Alarm Receiver -->
<receiver
android:name="com.MyApp.app.GCMHeartbeatAlarm">
</receiver>
With this code, the "Alarm Set!" log is hit, but the log in onReceive is never hit.
Anything that could help me figure out what's going on would be greatly appreciated!
Got it to work eventually. I'm not quite sure why, but using System.currentTimeMillis() wasn't working for the triggerAtMillis value. Perhaps it was because the alarm was set for ELAPSED_REALTIME instead of currentTimeMillis(), so the first alarm was never triggered. Instead I used SystemClock.elapsedRealtime() + 60 * 1000 to trigger the alarm beginning 1 minute after it is set, and then the onReceive method started being called in 1 minute intervals.
The working code was:
public void setAlarm(Context context) {
alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, GCMHeartbeatAlarm.class);
alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
alarmMgr.setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 60 * 1000, 60 * 1000, alarmIntent);
Log.e("GCMHeartbeat", "Alarm set!");
}
Related
my mean goal is to run a task periodically at midnight (00:00:00)
but the user can set the period based on the interval (daily, weekly , monthly)
let's assume that this job is a backup Scheduling.
this task will triggred at midnight but based on the user preference (midnight everyday, every week , or monthly ), and if the phone was in the doze mode or even off , wait untill the next start and start backup.
when i start implementing the code , i started with JobService and JobScheduler , but unfortunately i learned that i can set the repetitive but i can't set the exact time, so the last solution was to work with alarmmanager.
i use this code for triggering the alarm :
public static void setTheTimeToStartBackup(Context context,String periode) {
int DATA_FETCHER_RC = 123;
AlarmManager mAlarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Calendar calendar = Calendar.getInstance();
Intent intent = new Intent(context, BackUpAlarmRecevier.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, DATA_FETCHER_RC,intent, PendingIntent.FLAG_UPDATE_CURRENT);
long interval = 0;
switch (periode){
case "never":
return;
case "daily":
alarmStartTime.set(Calendar.HOUR_OF_DAY, 0);
interval = AlarmManager.INTERVAL_DAY;
break;
case "weekly":
alarmStartTime.set(Calendar.DAY_OF_WEEK, 1);
interval = AlarmManager.INTERVAL_DAY*7;
break;
case "monthly":
alarmStartTime.set(Calendar.WEEK_OF_MONTH, 1);
interval = AlarmManager.INTERVAL_DAY*30;
break;
}
alarmStartTime.set(Calendar.HOUR_OF_DAY, 0);
alarmStartTime.set(Calendar.MINUTE, 0);
alarmStartTime.set(Calendar.SECOND, 0);
mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP,calendar.getTimeInMillis(),interval, pendingIntent);
Log.e("Alarm","Set for midnight");
}
this is my receiver :
public class BackUpAlarmRecevier extends BroadcastReceiver {
SharedPreferences preferences;
#Override
public void onReceive(Context context, Intent intent) {
Log.e("BackUpAlarmReciver","Triggred");
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "TAG:APP");
wl.acquire();
sendNotification(context);// simple notification...
Toast.makeText(context, "Alarm !!", Toast.LENGTH_LONG).show();
startBackupProcess();
wl.release();
}}
the problem is task never start.
so i went to test it with less time interval (15min as the minimum possible as i read ), so i change my first function setTheTimeToStartBackup to this :
public static void setTheTimeToStartBackup(Context context,String periode) {
int DATA_FETCHER_RC = 123;
AlarmManager mAlarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.SECOND, 55);
Intent intent = new Intent(context, BackUpAlarmRecevier.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, DATA_FETCHER_RC,intent, PendingIntent.FLAG_UPDATE_CURRENT);
mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP,calendar.getTimeInMillis(),AlarmManager.INTERVAL_FIFTEEN_MINUTES, pendingIntent);
Log.e("Alarm","Set for midnight");
}
and exactly the same problem , nothing started, no log , no notification , nothing.
and i already set the Receiver in my manifest with all permission like that :
<receiver android:name=".job.BackUpAlarmRecevier"
android:enabled="true"
android:process=":remote"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
what im doing wrong in both cases ? and if it work , this code will persist for ever or i need to set it again each time ?
thanks :)
EDIT:
i call my function setTheTimeToStartBackup in the MainActivity.
You could set it to occur at midnight if you did the appropriate time calculations. Dynamically get the current time and date, calculate when to register the broadcast for the alarm manager. Customize the onReceive method to set another alarm at 12pm again.
Either way you can trigger a broadcast receiver by registering your receiver manually.
Broadcast receiver class:
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
System.out.println("Alarm received!! ");
// Register alarm again here.
}
}
Code to register a receiver with a custom intent filter.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AlarmManager mAlarmManager = (AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
getApplicationContext().registerReceiver(new AlarmReceiver(), new IntentFilter("AlarmAction"));
PendingIntent broadcast = PendingIntent.getBroadcast(this, 0, new Intent("AlarmAction"), 0);
// Add dynamic time calculations. For testing just +100 milli.
mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 100, broadcast);
;
}
You could achieve what you wanted through a background service.
My suggestion would be to turn the problem around a bit.
Create three topics on Firebase (daily, weekly, monthly). Subscribe users to appropriate topics. Have a Firebase function that is triggered by CRON job which sends the data notification down to the device, this data notification should schedule one-time WorkManager job for the update. This way you can control everything from server-side, if the phone is off, it will still execute as soon as it turns on and you don't need to manually take care of catching the Boot completed with alarm manager etc.
I have some existing code that spawns a service intent which does a bunch of stuff in the background. This code does work...
Intent serviceIntent = new Intent(context, APMService.class);
serviceIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startService(serviceIntent);
My question is: how to change this to use the AlarmManager.setInexactRepeating(...) methods?
I have changed the above code to this:
Intent serviceIntent = new Intent(context, APMService.class);
serviceIntent.putExtra("STARTED_BY", starter);
serviceIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//Set up recurring alarm that restarts our service if
// it crashes or if it gets killed by the Android OS
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pi = PendingIntent.getService(context, 0, serviceIntent, 0);
//am.cancel(pi);
am.setInexactRepeating(
AlarmManager.ELAPSED_REALTIME_WAKEUP //wake up the phone if it's asleep
, cal.getTimeInMillis()
, 10000
, pi);
And I have added these permissions to AndroidManifest.xml...
<uses-permission android:name="com.android.alarm.permission.SET_ALARM"/>
<uses-permission android:name="com.android.alarm.permission.WAKE_LOCK"/>
My understanding is that this is supposed to start the service immediately and then try to restart it again every 10 seconds. But this code isn't working properly.
Using this new code, the service never starts at all and I cannot see why not. To complicate matters the debugger never seems to attach to the app in time to see what's going on.
Can anyone see what I'm doing wrong?
Put AlarmManager code under onDestroy() function of service to schedule start of service as below:
#Override
public void onDestroy() {
/**
* Flag to restart service if killed.
* This flag specify the time which is ued by
* alarm manager to fire action.
*/
final int TIME_TO_INVOKE = 5 * 1000; // try to re-start service in 5 seconds.
// get alarm manager
AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AutoStartServiceReceiver.class);
PendingIntent pendingIntent = PendingIntent
.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
// set repeating alarm.
alarms.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() +
TIME_TO_INVOKE, TIME_TO_INVOKE, pendingIntent);
}
And handle starting of your service in AutoStartServiceReceiver as below:
public class AutoStartServiceReceiver extends BroadcastReceiver {
private static final String TAG = AutoStartServiceReceiver.class.getSimpleName();
#Override
public void onReceive(Context context, Intent intent) {
// check broadcast action whether action was
// boot completed or it was alarm action.
if (intent.getAction().equals(AppConstants.ACTION_ALARM_INTENT)) {
context.startActivity(new Intent(context, YourActivity.class));
// handle service restart event
LockerServiceHelper.handleServiceRestart(context);
}
}
}
Kindly note that, your service will not restart if you stop it manually from settings-apps-running apps-your app.
Your service is not starting because of AlarmManager.ELAPSED_REALTIME_WAKEUP, while it should be using AlarmManager.RTC_WAKEUP
If you want to run every 10s keep in mind that above API 21 alarm intervals below 60s are rounded up to 60s.
Also, consider using WakefulIntentService
https://github.com/commonsguy/cwac-wakeful
I've seen several examples on how to make some event to be repeated even when the app isnt running, but still I'm not sure if I got it.
With AlarmManager you can make your app to wake up to do something in some fixed interval without it consuming system resources between the periods, right?
But can it be to show up a toast over your current activity instead of having an Activity with a layout for it?
AlarmReceiver class:
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// this is where to start activity or service to launch toast message
}
}
In activity or boot receiver:
private static final int PERIOD = 60000; //or whatever you need for repeating alarm
AlarmManager mgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent alIntent = new Intent(context, AlarmReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, alIntent, 0);
mgr.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 60000, PERIOD, pi);
In AndroidManifest, add:
<receiver android:name=".AlarmReceiver"></receiver>
Not sure what is the issue here. The following code does not work. I have used pending intent before using similar code but never faced this issue. Kindly help.
The log statements from Main.java are getting printed but nothing gets logged from PublicData.class
Main.java
AlarmManager alarm;
long gap = 1800000;
Calendar cal;
Log.d("Stoned", "pending intent about to create");
Intent i = new Intent(this, PublicData.class);
PendingIntent pint = PendingIntent.getService(
this, 0, i, 0);
Log.d("Stoned", "pending intent creating");
alarm = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
alarm.setRepeating(AlarmManager.RTC, cal.getTimeInMillis(),
gap, pint);
Log.d("Stoned", "pending intent created");
PublicData.java
public class PublicData extends IntentService {
#Override
protected void onHandleIntent(Intent intent) {
Log.d("Stoned", "In pending intent");
}
.
.
The problem is that the second parameter is the offset, when the alarm will be called for the first time. If you use the current time this will take thousands of hours to first call...
alarm.setRepeating(AlarmManager.RTC, cal.getTimeInMillis(), gap, pint);
Should be (for immediate repeating)
alarm.setRepeating(AlarmManager.RTC, 0, gap, pint);
See also: http://developer.android.com/reference/android/app/AlarmManager.html#setRepeating%28int,%20long,%20long,%20android.app.PendingIntent%29
EDIT:
You can also use this:
long interval = DateUtils.SECOND_IN_MILLIS * 30;
long firstWake = System.currentTimeMillis() + interval;
am.setRepeating(AlarmManager.RTC_WAKEUP, firstWake, interval, pendingIntent);
In case anyone else is facing the same problem, the following thing worked for me. I added 'android:exported="true"' in the service declaration in the manifest. Also, I used the entire packagename instead of just the classname for the service. It worked after making these changes.
<service android:name="com.abc.def.PublicData" android:exported="true" >
I've been struggling with this for a couple of days. What I want to do is run a service periodically, about 2-3 minutes apart. I have an Activity that is responsible for the interface and setting up the first alarm.
The alarm is configured by a BroadcastReceiver which looks like this:
public class Receiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String message = "Alarm worked";
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
setAlarm(context);
}
public void setAlarm(Context context){
AlarmManager am = (AlarmManager) context.
getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, Receiver.class);
PendingIntent pi = PendingIntent.getBroadcast(context,
0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Intent dailyUpdater = new Intent(context, DiscoveryService.class);
context.startService(dailyUpdater);
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
+ (1000 * 30) , pi);
}
}
I've tried using setRepeating for AlarmManager, but it still has the same effect. What happens is that the AlarmManager works how it should, it fires an Intent which the receiver gets and executes onReceive periodically, as it should. However, it executes the service only the first time. After the first time, the alarms still go off, but the service is not executed.
I read some threads from people with similar problems, and one of them mentioned that PendingIntent lasts for only one send. Thus, I opted out to setting the alarm every time so I can set pendingIntent flag for updating every time.
I tried making my service an intentService, which is fine, but then my bluetooth scanner inside the service does not work because intentService thread terminates without waiting for my bluetooth discovery to finish.
Anyone have any idea what can help me?
Here is part of my service:
public class DiscoveryService extends Service {
public void onCreate() {
super.onCreate();
Toast.makeText(this, "MyAlarmService.onCreate()",
Toast.LENGTH_LONG).show();
findEverything();
}
}
EDIT: This is the code that I currently have.
public void onReceive(Context context, Intent intent) {
String message = "Alarm worked";
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
Intent dailyUpdater = new Intent(context, DiscoveryService.class);
context.startService(dailyUpdater);
}
public void setAlarm(Context context){
// get a Calendar object with current time
AlarmManager am=(AlarmManager)context.
getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, Receiver.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
(1000 * 30) , pi);
}
What happens is that the AlarmManager works how it should, it fires an Intent which the reciever gets and executes onReceive periodically, as it should. However, it executes the service only the first time. After the first time, the alarms still go off, but the service is not executed.
You are calling startService() once when you are scheduling the alarm. You are not calling startService() at all from your BroadcastReceiver. Yet you are scheduling the alarm via the BroadcastReceiver. Hence, when the alarm goes off, the service will not be sent a command, because you are not sending it a command.
I read some threads from people with similar problems, and one of them mentioned that PendingIntent lasts for only one send.
That is only if you use FLAG_ONE_SHOT.
Anyone have any idea what can help me?
Call startService() from your onReceive() method, instead of from your setAlarm() method. Also, add in all the WakeLock management logic, since you are using a _WAKEUP alarm and you are not able to use my WakefulIntentService.