Unable to cancel repeating alarm Android - android

I have read every question there is about Android, AlarmManager and cancelling.
I currently use an Activity starting a receiver through:
long msInterval = 1;
Intent intent = new Intent(this, Updater.class);
intent.setAction("theAction");
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 12, intent, 0);
Updater.origin = pendingIntent;
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (msInterval), msInterval, pendingIntent);
This starts the receiver Updater one millisecond after this code was called, with request code 12 (chosen arbitrarily, using 0 produces the same incorrect behaviour). It also sets the origin of Updater to the currently scheduled PendingIntent, which is later used to cancel the alarm.
Updater looks like this:
public class Updater extends BroadcastReceiver {
public static int flaggedClose = 0;
public static PendingIntent origin;
#Override
public void onReceive(Context context, Intent intent) {
// Do some work
Log.w("running", "run");
if (Updater.flaggedClose != 0) {
if(flaggedClose == 1) {
Toast.makeText(context, "Finished!", Toast.LENGTH_SHORT).show();
}
flaggedClose++; // Only show Toast once
Log.w("running", "close");
origin.cancel();
AlarmManager alarms = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
alarms.cancel(origin);
}
}
}
What it does at the moment is just to log the message "run", which is done ~1000 times/s. When the Activity's onStop() is called, Updater.flaggedClose is set to 1. I can be see this in Logcat since it starts printing the log warning "close". However, the alarm is still on, so every other logged message is "run" and every other is "close". In best case, the alarm is closed after a few seconds. Worst case I need to restart the phone. In the description of AlarmManager, it specifically states that close closes "Any alarm, of any type, whose Intent matches this one (as defined by filterEquals(Intent)), will be canceled". Why are there still alarms being triggered?

As ci_ mentioned in the comments, it is possible that "those 100 "extra" alarms already triggered before the cancel happens". For anyone else who has the same problem, here is a solution. I tested the AlarmManager and it seems to work best if you have a delay of at least 200 ms. for a lower delay use a Handler. Example from the question using Handler:
public class MainActivity extends Activity {
private boolean pressed = false;
private boolean done = false;
private Handler worker;
private Runnable method;
long msInterval = 1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
worker = new Handler();
method = getMethod();
Button toggle = (Button)(findViewById(R.id.toggle));
toggle.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if(!pressed) {
worker.post(method);
pressed = true;
} else {
done = true;
}
}
});
}
#Override
protected void onDestroy() {
super.onDestroy();
done = true;
}
private Runnable getMethod() {
return new Runnable() {
public void run() {
Log.w("running", "run");
if (done) {
Toast.makeText(getApplicationContext(), "Finished!", Toast.LENGTH_SHORT).show();
Log.w("running", "close");
} else {
worker.postDelayed(method, msInterval);
}
}
};
}
}
On first button press the handler starts the runnable, and on each call the runnable calls itself. On second button press condition done is set to true, thus the runnable finishes after one (cleanup) round.

Related

Android alarm triggers repeatedly

I am trying to set an alarm to call a method in MainActivity. I have used the method described here; the alarm fires but once it does it repeats about once a second.
I am using setExactAndAllowWhileIdle since the alarm is needed only every hour or so (actually it doesn't need to be exact, I could use setAndAllowWhileIdle instead but that gives the same problem).
My Alarm class is pretty simple:
public class Alarm extends BroadcastReceiver
{
static MainActivity main = null;
public Alarm()
{
}
public Alarm(MainActivity ctx)
{
main = ctx;
}
#Override
public void onReceive(Context context, Intent intent)
{
if (main != null)
main.alarmAction();
}
}
In OnCreate() for MainActivity I have
alarmReceiver = new Alarm(this);
IntentFilter alarmFilter = new IntentFilter("Alarm");
registerReceiver(alarmReceiver,alarmFilter);
and then I have methods:
public void SetAlarm() {
alarmStarted = true;
Intent i = new Intent(this, Alarm.class);
i.setAction("Alarm");
PendingIntent pi = PendingIntent.getBroadcast(this, 1001, i, 0);
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
am.cancel(pi); // tried this to solve the problem but probably not needed
am.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME, 1000 * 60 * 10, pi);
}
public void alarmAction() {
if (!alarmStarted)
return;
SetAlarm();
// will be more stuff here but this is minimum required to show problem
}
The flag alarmStarted is set from a button press.
Android Studio is giving me a memory-leak warning about the use of static in static MainActivity main = null, but without this main is always null in onReceive. I don't know if the repeating alarm issue is connected with this or not.

How to make an Activity alive for certain time and after that another activity starts?

how to create an activity that is restricted to time. I would like when I launch my app, an initial activity appears and lasts only for few seconds just to display the app name and the its version and some other info and after a the designated time, the app starts. I have done such thing but the initial activity had had an animation, and when the animation ends then the new activity or the app starts. But, now my initial activity has no animation and I do not know how to keep an activity active/live for 10 seconds, for an example, and when the 10 seconds end, another app starts?
Use a handler to wait (for example 3 seconds) and move on:
private static int SPLASH_TIME_OUT = 3000;
new Handler().postDelayed(new Runnable() {
/*
* Showing splash screen with timer running. This is useful to showcase your app logo/company or something like that.
*/
#Override
public void run() {
// This method will be executed once the timer is over
// Start your app main activity
Intent i = new Intent(SplashScreen.this, MainActivity.class);
startActivity(i);
// close this activity
finish();
}
}, SPLASH_TIME_OUT);
You can use a Timer:
private Timer timer;
#Override
protected void onResume (){
super.onResume();
timer = new Timer();
timer.schedule(new OpenActivityTask(), SOME_TIME_IN_MILLIS);
}
#Override
protected void onPause (){
super. onPause();
timer.cancel();
timer = null;
}
private static class OpenActivityTask extends TimerTask {
#Override
public void run() {
//TODO Go to new activity
}
}
It provides cancellation in case you manually close the activity before the delay time is reached, or if it goes to background for some reason.
Another option is to use the AlarmManager and schedule a one-shot PendingIntent. This approach is probably shorter in code and also allows for safe cancellation:
PendingIntent pi;
#Override
protected void onResume (){
super.onResume();
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, YourOtherActivity.class);
pi = PendingIntent.getActivity(this, 0, intent, 0);
alarmManager.set(AlarmManager.RTC, System.currentTimeMillis() + SOME_TIME_IN_MILLIS, pi);
}
#Override
protected void onPause (){
super. onPause();
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(pi);
}

Disable the Asynchronous display of a Toast [duplicate]

This question already has answers here:
How to stop displaying message from Toast when Application is closed?
(5 answers)
Closed 9 years ago.
Assume that you have a Button that whenever you click on it, it displays a Toast with the message "Hello".
If you decide to click on that Button 20 times repeatedly, the Toasts will be displayed asynchronously, waiting each one its turn. However, this is not what I really want.
What I want is the following:
Whenever I press the Button, I want it to cancel the previous displayed Toast and display the actual one. So that when I close the app, no Toasts will be displayed if ever the user decides to mess with the app by clicking on the Button 100 times within a very small period.
You'll need to declare your Toast at a class level, and then call toast.cancel() before constructing a new Toast object and showing it.
public class XYZ extends Activity {
Toast mToast;
public void onCreate(Bundle b) {
.....
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if(mToast != null)
mToast.cancel();
mToast = Toast.makeText.....;
}
});
}
}
Here' another solution. If all you want is to prevent multiple toasts from being displayed for fast clicks then a combination of AlarmManager and a PendingIntent should work too. Now bear in mind, I haven't tested this and haven't checked if it compiles.
AlarmManager mAlarm;
PendingIntent mPendingIntent;
//toast delay for a second
int toastDelay = 1000;
#Override
public void onCreate (Bundle savedInstanceState) {
Intent i = new Intent(context, MySimpleBroadcastReceiver.class);
//optionally set an action
i.setAction("show:toast");
mPendingIntent = PendingIntent.getBroadcast(context, 0, i, PendingIntent.FLAG_CANCEL_CURRENT);
mAlarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
}
public void onClick(View v) {
//set the alarm to trigger 1 second from current time
mAlarm.set(AlarmManager.RTC_WAKEUP, (System.currentTimeMillis() + toastDelay), mPendingIntent);
}
#Override
protected void onDestroy () {
if (mAlarm != null) {
mAlarm.cancel(mPendingIntent);
}
mAlarm = null;
mPendingIntent = null;
}
Create the broadcast receiver and remember to add it to your AndroidManifest.xml:
public class MySimpleBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive (Context context, Intent intent) {
//optionally check the action that triggered the broadcast..useful if you want to use this broadcast for other actions
if (intent.getAction().equals("show:toast")) {
Toast.makeText(context, "Hello", Toast.LENGTH_SHORT).show();
}
}
}
You can read about PendingIntent.FLAG_CANCEL_CURRENT.

What kind of Service do I need? Custom IntentService?

I'm designing my first Android app.
This app consist in several Runnable that do some stuff. Initially I made this Runnable to be execute by a Thread (a Thread for each Runnable). Each Runnable is also Observable, so it can notify changes to Activity. User click on a start button, one or more Runnable starts, them do their job notifying gui during execution and then stops. All works fine.
First question: Is that approach the right one? In order to answer this question please keep reading.
I need two other things in my app:
to be sure that execution of my jobs doesn't stops, even if user goes away from my app to do something else;
to plan the execution of my Runnable that has to start and execute in background. Example: user decides that wants a "job" to be execute everyday at 16:00.
I've seen that I can do that with an AlarmManager and Service.
Second question: I need a Service that can manage several Runnable asynchronously, so when AlarmManager starts I ask this Service to do the requested job; I'll also modify the first part of application: instead of Thread I'll use this Service, so I can be sure that execution doesn't stop.
What kind of Service I need? IntentService can do this job?
It's right to proceed in this way? There is a better solution?
Can you give me some example of how I can implement all that?
I hope that I explained clearly my situation, otherwise I'll try to do it better.
Regards
First question: Is that approach the right one?
No, you should implement and run your Runnables in Threads in a Service.
An IntentService would be your best option if you don't require your Service to handle multiple requests simultaneously. If you start a Service it will keep running in the background even if the Activity that started it goes to the background or stops.
A Runnables can send a broadcast indicating a UI update is needed. The Activity should register a BroadcastReceiver to listen to the broadcast message and update the UI accordingly.
You can use an AlarmManager to schedule the execution of your jobs as you indicated. One way to do it is to schedule the AlarmManager to send a broadcast to be received by your IntentService which acts upon it by running the appropriate job.
Here is an example that combines all that:
Here is the IntentService
public class MyIntentService extends IntentService {
public static final String ACTION_START_JOB = "com.mycompany.myapplication.START_JOB";
public static final String ACTION_UPDATE_UI = "com.mycompany.myapplication.UPDATE_UI";
private final IBinder mBinder = new MyBinder();
// You can have as many Runnables as you want.
Runnable run = new Runnable() {
#Override
public void run() {
// Code to run in this Runnable.
// If the code needs to notify an Activity
// for a UI update, it will send a broadcast.
Intent intent = new Intent(ACTION_UPDATE_UI);
sendBroadcast(intent);
}
};
public MyIntentService() {
super("MyIntentService");
}
#Override
public void onCreate() {
// You need to register your BroadcastReceiver to listen
// to broadcasts made by the AlarmManager.
// The BroadcastReceiver will fire up your jobs when these
// broadcasts are received.
IntentFilter filter = new IntentFilter(ACTION_START_JOB);
registerReceiver(jobBroadcastReceiver, filter);
}
#Override
public void onDestroy() {
// You should unregister the BroadcastReceiver when
// the Service is destroyed because it's not needed
// any more.
unregisterReceiver(jobBroadcastReceiver);
}
/**
* This method is called every time you start this service from your
* Activity. You can Spawn as many threads with Runnables as you want here.
* Keep in mind that your system have limited resources though.
*/
#Override
protected void onHandleIntent(Intent intent) {
Intent intentFireUp = new Intent();
intentFireUp.setAction(ACTION_START_JOB);
PendingIntent pendingIntentFireUpRecording = PendingIntent
.getBroadcast(MyIntentService.this, 0, intentFireUp, 0);
AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Calendar cal = Calendar.getInstance();
int year = 2013, month = 5, day = 10, hourOfDay = 7, minute = 13, second = 0;
cal.set(year, month, day, hourOfDay, minute, second);
long startTime = cal.getTimeInMillis() + 5 * 60 * 1000; // starts 5
// minutes from
// now
long intervalMillis = 24 * 60 * 60 * 1000; // Repeat interval is 24
// hours (in milliseconds)
// This alarm will send a broadcast with the ACTION_START_JOB action
// daily
// starting at the given date above.
alarm.setRepeating(AlarmManager.RTC_WAKEUP, startTime, intervalMillis,
pendingIntentFireUpRecording);
// Here we spawn one Thread with a Runnable.
// You can spawn as many threads as you want.
// Don't overload your system though.
new Thread(run).run();
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
// Depending on your implementation, you may need to bind
// to this Service to run one of its methods or access
// some of its fields. In that case, you will need a Binder
// like this one.
public class MyBinder extends Binder {
MyIntentService getService() {
return MyIntentService.this;
}
}
// Spawns a Thread with Runnable run when a broadcast message is received.
// You may need different BroadcastReceivers that fire up different jobs.
BroadcastReceiver jobBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
new Thread(run).run();
}
};
}
And here is the Activity
public class MyActivity extends Activity {
Service mService;
boolean mBound = false;
ToggleButton mButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = (ToggleButton) findViewById(R.id.recordStartStop);
mButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if (mButton.isChecked()) {
Intent intent = new Intent(MyActivity.this,
MyIntentService.class);
startService(intent);
}
}
});
}
#Override
protected void onStart() {
super.onStart();
}
#Override
protected void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter(MyIntentService.ACTION_UPDATE_UI);
registerReceiver(uiUpdateBroadcastReceiver, filter);
}
#Override
protected void onPause() {
super.onPause();
unregisterReceiver(uiUpdateBroadcastReceiver);
}
BroadcastReceiver uiUpdateBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Here goes the code to update your User Interface
}
};
ServiceConnection myServiceConnection = new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
mBound = false;
}
// If you need
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MyIntentService mService = ((MyBinder) service).getService();
mBound = true;
}
};
}
And don't forget to add the Service definition in your AndroidManifest.xml file:
<manifest ... >
...
<application ... >
<service android:name=".MyIntentService" />
...
</application>
</manifest>

Start Android Service after every 5 minutes

I was searching over the internet for last 2 days but I couldn't find any tutorial helpful. I have created a service and I am sending a notification in status bar when the service starts. I want that service to stop after showing the notification and start it again after 5 minutes. Please let me know if it is possible and provide me some helpful tutorials if you have any. I heard of TimerTask and AlarmManager and I tried to use them as well but I wasn't able to get the desired result.
EDIT: I need the service to be started every 5 minutes even if my application is not running.
You do not want to use a TimerTask since this depends on your application running continuously. An AlarmManager implementation makes it safe for your application to be killed between executions.
Stating that you tried to use AlarmManager but did not get the desired result is not a helpful statement, in that it tells no one how to help you to get it right. It would be much more useful to express what happened.
http://web.archive.org/web/20170713001201/http://code4reference.com/2012/07/tutorial-on-android-alarmmanager/ contains what appears to be a useful tutorial on AlarmManager. Here are the salient points:
1) Your alarm will cause an Intent to fire when it expires. It's up to you to decide what kind of Intent and how it should be implemented. The link I provided has a complete example based on a BroadcastReceiver.
2) You can install your alarm with an example such as:
public void setOnetimeTimer(Context context) {
AlarmManager am=(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmManagerBroadcastReceiver.class);
intent.putExtra(ONE_TIME, Boolean.TRUE);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (1000 * 60 * 5), pi);
}
Below I have provided three files, MainActivity.java for start service, Second file MyService.java providing service for 5 Minute and Third is manifest file.
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService(new Intent(this, MyService.class)); //start service which is MyService.java
}
}
MyService.java
public class MyService extends Service {
public static final int notify = 300000; //interval between two services(Here Service run every 5 Minute)
private Handler mHandler = new Handler(); //run on another Thread to avoid crash
private Timer mTimer = null; //timer handling
#Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public void onCreate() {
if (mTimer != null) // Cancel if already existed
mTimer.cancel();
else
mTimer = new Timer(); //recreate new
mTimer.scheduleAtFixedRate(new TimeDisplay(), 0, notify); //Schedule task
}
#Override
public void onDestroy() {
super.onDestroy();
mTimer.cancel(); //For Cancel Timer
Toast.makeText(this, "Service is Destroyed", Toast.LENGTH_SHORT).show();
}
//class TimeDisplay for handling task
class TimeDisplay extends TimerTask {
#Override
public void run() {
// run on another thread
mHandler.post(new Runnable() {
#Override
public void run() {
// display toast
Toast.makeText(MyService.this, "Service is running", Toast.LENGTH_SHORT).show();
}
});
}
}
}
AndroidManifest.xml
<service android:name=".MyService" android:enabled="true" android:exported="true"></service>
Create a Timer object and give it a TimerTask that performs the code you'd like to perform.
Timer timer = new Timer ();
TimerTask hourlyTask = new TimerTask () {
#Override
public void run () {
// your code here...
}
};
// schedule the task to run starting now and then every hour...
timer.schedule (hourlyTask, 0l, 1000*60*60); // 1000*10*60 every 10 minut
The advantage of using a Timer object is that it can handle multiple TimerTask objects, each with their own timing, delay, etc. You can also start and stop the timers as long as you hold on to the Timer object by declaring it as a class variable or something.

Categories

Resources