Using Alarm Manager to execute a task once - android

I am trying to execute an action once at a later time using AlarmManager. I followed the code and the question here and came up with this.
public class EmailAccountUpdater extends BroadcastReceiver
{
// Constructors
public void onReceive(Context context, Intent intent)
{
if (intent.getAction().equals(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION))
{
Log.v("Test", " Step 1 - Creating the alarm " );
// Place holder
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent newIntent = new Intent("com.test.EMAIL_ACCOUNTS_CHANGED");
PendingIntent pendingIntent = PendingIntent.getBroadcast( context, 0, newIntent, PendingIntent.FLAG_CANCEL_CURRENT);
alarmManager.set( AlarmManager.RTC_WAKEUP, 35000, pendingIntent);
}
}
}
AlarmReceiver.java
public class AlarmReceiver extends BroadcastReceiver
{
// constructors
#Override
public void onReceive(Context context, Intent intent)
{
Log.v("Test","Step 2 - Alarm received");
if (intent.getAction().equals("com.test.EMAIL_ACCOUNTS_CHANGED"))
{
onAccountsUpdated();
}
}
public void onAccountsUpdated()
{
// do something
}
}
In the manifestManifest.xml
<receiver android:name="full.path.AlarmReceiver">
<intent-filter>
<action android:name="com.test.EMAIL_ACCOUNTS_CHANGED"/>
</intent-filter>
</receiver>
Basically what I wanted to do was to put the following in Placeholder (just below the first log statement).
Thread.sleep(35000);
onAccountsUpdated();
But according to this, it is not suggestible to use postDelayed and Thread.sleep in BroadcastReceiver. So I came up with this. What happens is I always get the Step 1 but never reach the step 2. What I am I doing wrong? Any help would be welcome.

The solution is (as per the thread you linked):
you want something to happen some time after the broadcast you can start a service, and that service wait the amount of time, or if the amount of time you want to wait is longer than a few seconds, you should just put the launch of this service in the AlarmManager and let the AlarmManager launch the service for you.
Your plan doesn't work because the context is destroyed after EmailAccountUpdater.onReceive returns.

Related

How to design a task recurrently every 10 minutes in Android?

I hope to do a restore operation recurrently every 10 minutes, and I hope that the function will be keep live even if I restart my mobile phone.
The following content if my planning, I don't know whether it's right, or do you have more better way?
Step 1: Invoke EnableCleanupService(Context mContext) in a Activity, the system will do a restore operation recurrently every 10 minutes, the system will keep to do a restore operation recurrently every 10 minutes even if I close the APP, right?
Step 2: In order to do the restore operation recurrently every 10 minutes after I restart my mobile phone, the system invoke EnableCleanupService(context) on onReceive(Context context, Intent intent) automatically even if I don't open the App. Right?
Step 1
public static void EnableCleanupService(Context mContext){
AlarmManager alarmManager;
alarmManager = (AlarmManager)mContext.getSystemService(mContext.ALARM_SERVICE);
long now = System.currentTimeMillis();
now= now+mContext.getResources().getInteger(R.integer.FirstTigger)*60*1000;
int nextTime=mContext.getResources().getInteger(R.integer.ScheduleTimeMin)*60*10*1000;
alarmManager.setRepeating(
AlarmManager.RTC_WAKEUP,
now,
nextTime,
GetPendingIntent(mContext)
);
}
private static PendingIntent GetPendingIntent(Context mContext){
PendingIntent pendingIntent=null;
pendingIntent = PendingIntent.getService(mContext,
0,
new Intent(mContext, CleanupService.class),
PendingIntent.FLAG_CANCEL_CURRENT);
return pendingIntent;
}
public class CleanupService extends IntentService {
#Override
protected void onHandleIntent(Intent intent) {
//Do Task
}
}
Step 2
<receiver android:name="bll.CleanupBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
public class CleanupBootReceiver extends BroadcastReceiver{
private static final String ACTION = "android.intent.action.BOOT_COMPLETED";
#Override
public void onReceive(Context context, Intent intent) {
if (intent != null && intent.getAction() != null && ACTION.compareToIgnoreCase(intent.getAction()) == 0) {
EnableCleanupService(context)
}
}
}
What you have done seems to be correct.In my opinion AlarmManager is the best if you want to perform any time-based or time-bound operation. If you have any doubts regarding AlarmManager. You can read this Working with AlarmManager | With Example
One important thing though. Setting a repetitive Alarm every 10 minutes is a very bad experience, it drains the battery and also some custom cleaner applications might cancel your Alarm as "spam".

How to run a Jobscheduler or a Service every minute wihout stopping?

I'm doing an Android app that requires sending its location frequently, every 1 minute or 2 minutes at the most. For this, I use a JobSchedulerService. I've already managed to make it run more than once every 15 minutes on devices with Android N version by replacing the .setPeriodic() with a .setMinimumLatency(). The fact is that at the beginning it is executed periodically in the established time, but after a while it runs every 7 or 9 minutes approximately.
I have already included the application in the battery saving white list, but didn't work. Is there any way to execute it or a similar service every minute with no restrictions? Doesn't matter how much battery the app spends.
EDIT:
This is what I've tried:
ReceiverService:
public class ReceiverService extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context ctx, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
if (!isMyServiceRunning(ServiceBackground.class, ctx))
startWakefulService(ctx, new Intent(ctx, ServiceBackground.class));
new ServiceAlarmManager(ctx).register();
}
}
private boolean isMyServiceRunning(Class<?> serviceClass,Context context) {
ActivityManager manager = (ActivityManager)context. getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
Log.i("Service already","running");
return true;
}
}
Log.i("Service not","running");
return false;
}
}
The ServiceAlarmManager is exactly the same as #madking said.
You can put your code that sends location in a Service and implement an AlarmManager that periodically checks if your Service is running and restarts it if the Service has been killed by OS. You'll have to implement the AlarmManager using a WakefulBroadcastReceiver.
ReceiverService.java
public class ReceiverService extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context ctx, Intent intent) {
if (!YourService.isRunning()) {
startWakefulService(ctx, new Intent(ctx, YourService.class));
}
new ServiceAlarmManager(ctx).register();
}
}
ServiceAlarmManager.java
public class ServiceAlarmManager {
private Context ctx;
private static final int TIME_INTERVAL = 300 * 1000;
public ServiceAlarmManager(Context context) {
ctx = context;
}
public void register() {
Intent serviceRestarter = new Intent();
serviceRestarter.setAction("someString");
PendingIntent pendingIntentServiceRestarter = PendingIntent.getBroadcast(ctx, 0, serviceRestarter, 0);
AlarmManager alarmManager = (AlarmManager) ctx.getSystemService(ctx.ALARM_SERVICE);
Date now = new Date();
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, now.getTime() + TIME_INTERVAL, pendingIntentServiceRestarter);
}
}
Also register your BroadcastReceiver in your Manifest.xml file
<receiver android:name=".ReceiverService">
<intent-filter>
<action android:name="someString" />
</intent-filter>
</receiver>
The register() method does two things.
1- Issues a broadcast which is caught by WakefulBroadcastReceiver and restarts the Service if required
2- Sets the next alarm to be invoked to check if the Service has been killed.
This way the service keeps running even if the OS kills it and you'll be able to send location updates periodically.
Note: Though this practice is not recommended as your application will use more battery but you don't seem to care about it as I did not either as some business requirements don't leave us a choice.
I tried this and it works: in the onCreate() of your activity you schedule an Alarm for every minute (setAlarm). Everytime the alarm is triggered, WakefulBroadcastReceiver is called, and that's where we launch our service(s):
private static long INTERVAL_ALARM = 1 * 60 * 1000;
public static void setAlarm(Context context) {
long current_time = Calendar.getInstance().getTimeInMillis();
Intent myAlarm = new Intent(context.getApplicationContext(), AlarmReceiver.class);
PendingIntent recurringAlarm = PendingIntent.getBroadcast(context.getApplicationContext(), 0, myAlarm, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarms = (AlarmManager) context.getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarms.setRepeating(AlarmManager.RTC_WAKEUP, current_time, INTERVAL_ALARM, recurringAlarm);
}
And in the receiver:
public class AlarmReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent myService = new Intent(context, MyService.class);
context.startService(myService);
}
}
In your service, you should stopSeflf() in the end of your treatment.
Don't forget to register your BroadcastReceiver in your Manifest.xml file
NB: WakefulBroadcastReceiver is deprecated in API level 26.1.0. JobSchedulerService does the work

AlarmManager alarm start when system time is changed by user?

i am using AlarmManager class for setting Alarms it is working fine.
But if i set alarm like 9pm and current time is 8pm and i changed the system time to 10pm
then alarm 9pm alarm start automatically. so to solve this issue
i have searched so much but did not found any good answer
Please help
here is my code for alarm setting
final int id = (int) System.currentTimeMillis();
Intent intent = new Intent(this, AlarmReceiver.class);
intent.putExtra("requestCode", id);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, id, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 2*60*1000, pendingIntent);
One of the options is to store all set alarms in database, then create a BroadcastReceiver which will listen for ACTION_TIME_CHANGE action. When user changes time it will be triggered. Then create a IntentService which will be responsible for resetting alarms. In this service class:
Read db and identify all passed alarms.
Cancel passed alarms
Set alarms for next day
Your code may look like as below:
In your Manifest:
<uses-permission android:name="android.permission.ACTION_TIME_CHANGE"/>
and below activities:
<receiver android:name=".TimeChangedReceiver" android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.TIME_SET" />
</intent-filter>
</receiver>
<service android:name=".RestartAlarmsService"/>
Create class "TimeChangedReceiver" inside of which:
public class TimeChangedReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if("android.intent.action.TIME_SET".equals(intent.getAction())) {
Intent i = new Intent(context, RestartAlarmsService.class);
ComponentName service = context.startService(i);
}
}
}
Create "RestartAlarmsService" class inside of which:
public class RestartAlarmsService extends IntentService {
public RestartAlarmsService() {
super("RestartAlarmsService");
}
#Override
protected void onHandleIntent(Intent intent) {
// read db here
// then cancel passed alarms
// reset them to next day
}
}
You can find many tutorials on how to use Databases and implement it in your code. Hope my answer is somehow helpful.
yes it will give you broadcast, as your pending intent object is still attached to that event while you change time that is greater than you alarm firing time.
solution- validate your condition while you receive broadcast from alarm manager

Keep service alive after app has been destroyed

I'm developing an app that need to do some check in the server every certain amount of time. The check consist in verify if there are some notification to display. To reach that goal I implemented Service, Alarm Manager and Broadcast Reciever. This is the code that I'm using so far:
public class MainActivity {
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
setRecurringAlarm(this);
}
/**
*
* #param context
*/
private void setRecurringAlarm(Context context) {
Calendar updateTime = Calendar.getInstance();
Intent downloader = new Intent(context, MyStartServiceReceiver.class);
downloader.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, downloader, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, updateTime.getTimeInMillis(), 60000, pendingIntent);
}
...
}
Receiver class
public class MyStartServiceReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent dailyUpdater = new Intent(context, MyService.class);
context.startService(dailyUpdater);
Log.e("AlarmReceiver", "Called context.startService from AlarmReceiver.onReceive");
}
}
Service class
public class MyService extends IntentService {
public MyService() {
super("MyServiceName");
}
#Override
protected void onHandleIntent(Intent intent) {
Log.e("MyService", "Service running!");
// TODO Do the hard work here
this.sendNotification(this);
}
private void sendNotification(Context context) {
// TODO Manage notifications here
}
}
Manifest.xml
<!--SERVICE AND BROADCAST RECEIVER-->
<service
android:name=".MyService"
android:exported="false"/>
<receiver
android:name=".MyStartServiceReceiver"
android:process=":remote"/>
The code works fine, the task in the service will be excecuted periodically. The problem is that the service is destroyed when the app is forced to close. I need to keep alive the service, capable to execute the task, even if the user has closed the app, so the user can be updated via notifications. Thank you for your time!
You can't. If the app is forced closed, that means either its crashed (in which case the service has to be stopped as it may no longer work correctly) or the user force closed it in which case the user wants the app to stop- which means the user doesn't want the service to run. Allowing a service to be automatically restarted even if the user stops it would be basically writing malware into the OS.
In fact, Android went the exact opposite (and correct) way- if the user force stops an app, NOTHING of the app can run until the user runs it again by hand.
You may go through this. I hope this will solve your problem. If you want to keep awake your service it is practically not possible to restart the app which is forced close. So if you disable force stop your problem may be solved.

Start service periodically with AlarmManager

According to these examples: here and here, I was trying to create Service which starts periodically.
First I created Service:
public class MonitorService extends IntentService {
private static final String TAG = "MonitorService";
public MonitorService() {
super(TAG);
}
#Override
protected void onHandleIntent(Intent intent) {
Log.d("TAG", "Service method was fired.");
}
}
Next I created Receiver:
public class MyReceiver extends BroadcastReceiver {
private static final String TAG = "MyReceiver";
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "MyReceiver on receive");
Intent i = new Intent(context, MonitorService.class);
context.startService(i);
}
}
I added starting method for this in MainActivity:
public void scheduleAlarm() {
Intent intent = new Intent(getApplicationContext(), MyReceiver.class);
final PendingIntent pIntent = PendingIntent.getBroadcast(this, 0,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
long firstMillis = System.currentTimeMillis();
AlarmManager alarm = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
// 1s is only for testing
alarm.setInexactRepeating(AlarmManager.RTC_WAKEUP, firstMillis, 1000, pIntent);
}
which is calling of course in onCreate method.
And I didn't forget to change AndroidManifest:
<receiver
android:name=".MyReceiver"
android:process=":remote" >
</receiver>
<service
android:name=".MonitorService"
android:exported="false" />
And unfortunately the result is that nothing happens in my logs.
So I have two questions.
QUESTION
How to solve issue with not starting service?
If I add scheduleAlarm method to onCreate it will be calling every time I start my application, what is the best way to start this method only for the first time application is started?
EDIT
According to #Lasse hints, I started debugging, and realized that Log.d is not working, when I changed it to Log.i, information from MonitorService was logged.
But... debugging is not stoping on breaking point in MyReceiver, and changing Log.d to Log.i there didn't help. Of course MonitorService is firing, weird thing.
Also time with 1000 ms results in firing service every minute, maybe it's minimum time, and changing to AlarmManager.INTERVAL now doesn't matter.
EDIT 2
Finally I'm getting logs from both service and receiver. I had tried many times and after that it is working, but I don't know why.
But with that another problem has appeared - I'm getting warning when my Service is running
W/art: Suspending all threads took: 21.787ms
I thought that Service is running background so it doesn't matter how long it is, should I concern about this warning?
Edited
Regarding the first question :
See this from the developer website
setInexactRepeating(), you have to use one of the AlarmManager interval constants--in this case, AlarmManager.INTERVAL_DAY.
So change your 1000 to use of of the constans
Regarding your other question you could override the application object and start it there. This way it is only called when launching the app.

Categories

Resources