I am trying to implement an AlarmManager using an inner BroadcastReceiver class. Here is my inner class:
public class MyAlarm extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "alarm worked", Toast.LENGTH_SHORT).show();
}
public void setAlarm(Context context) {
AlarmManager am=(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(context, MyAlarm.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 10000, pi);
}
}
Since its an inner class I understand that I have to dynamically register the receiver. In my main activity I have the following code:
IntentFilter filter = new IntentFilter();
MyAlarm alarm = new MyAlarm();
alarm.setAlarm(this);
this.registerReceiver(alarm, filter);
The alarm isn't firing for me.
As far as I know, you can't use a LocalBroadcastReceiver with AlarmManager, because with localBroadcast the data doesn't leave the application, it works within the app process (local), and the alarm manager works with different processes.
Actually the pending intent would be the "problem" here:
The PendingIntent class provides a mechanism for creating intents that
can be fired on your application's behalf by another application at a
later time
As you can see by definition the pendingIntent works with different processes. That would be the reason you cannot make it work with a local broadcast.
You would need to register the receiver in the manifest, and as #leesei said, you have to declare the inner class as static.
Not knowing the details of your project, I'm inclined to say don't use a receiver, use a Handler and post a delayed runnable while your activity is in the foreground.
private static final long DELAY = 10000;
private Handler handler;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler = new Handler();
}
protected void onStart() {
handler.postDelayed(doSomethingRunnable, DELAY);
}
protected void onStop() {
handler.removeCallbacks(doSomethingRunnable);
}
private Runnable doSomethingRunnable = new Runnable() {
#Override
public void run() {
doSomething(); // the method you wanted to call
handler.postDelayed(this, DELAY);
}
}
Docs: IntentService
public class MyService extends IntentService() {
private static final String TAG = "MyService"
public MyService() {
super(TAG);
}
#Override
public void onHandleIntent(Intent intent) {
// do your work here
}
}
Somewhere else, possibly in your activity, you will create an alarm to run your service periodically:
AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(context, MyAlarm.class);
PendingIntent pi = PendingIntent.getService(context, 0, i, 0);
am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 10000, pi);
I'm not sure what your use case it, but once every ten seconds seems a bit aggressive. You may want to consider a longer interval.
use this code in your onreceive method of Broadcastreceiver-
#Override
public void onReceive(Context context, Intent intent)
{
//you need to acquire the power lock
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "YOUR TAG");
wl.acquire();
//do you things
//then release the lock
wl.release();
}
Related
I have AlarmManager and need to perform work on Background, to download JSON obj. if have new data in db. Can I make instead running a service (I don't need service to running all the time, I need just to download data and that's it. But I need that periodically every 1 or more minutes.
I was thinking of using JobScheduler combining with AlarmManager but I think this is not good approach
Sometimes after some time service is killed by OS and AlarmManager not starting again.
MainActivity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
alarmMgr = (AlarmManager)this.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, NotificationBroadcastReceiver.class);
PendingIntent alarmIntent = PendingIntent.getBroadcast(this, 1, intent, 0);
// Hopefully your alarm will have a lower frequency than this!
alarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + INTERVAL_ONE_MINUTE,
INTERVAL_ONE_MINUTE, alarmIntent);
Log.d("Alarm", "Started");
}
BroadcastReceiver
public class NotificationBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, ExampleJobService.class);
context.startService(i);
}
}
IntentService class
public class ExampleJobService extends IntentService {
private void doBackgroundWork() {
new Thread(new Runnable() {
#Override
public void run() {
//some work here
}
}
}
private void downloadJSON(final String urlWebService) {
class DownloadJSON extends AsyncTask<Void, Void, String> {
//some work here
}
}
}
Tasks run on Android in the background, kill more than 10 seconds
To solve this problem
Use the foreground service to resolve this issue
Please read this page
link
I'm currently working with Android Alarm Manager and found a working example. But it does not work properly in my situation. Let me explain. Basically my goal is to execute a method from the MainActivity each 5 mins. For this purpose I use Alarm Manager to schedule that task.
Basically this is the working stuff:
AlarmReceiver.java
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
context.sendBroadcast(new Intent("SERVICE_TEMPORARY_STOPPED"));
}
}
MainActivity.java
public class MainActivity extends Activity{
private PendingIntent pendingIntent;
private AlarmManager manager;
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "I'm running", Toast.LENGTH_SHORT).show();
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent alarmIntent = new Intent(this, AlarmReceiver.class);
pendingIntent = PendingIntent.getBroadcast(this, 0, alarmIntent, 0);
registerReceiver(broadcastReceiver, new IntentFilter("SERVICE_TEMPORARY_STOPPED"));
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startAlarm();
}
});
}
public void startAlarm() {
manager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
int interval = 300000;
manager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), interval, pendingIntent);
Log.d(TAG, "Alarm Set");
}
}
Everything is good. "I'm running" Toast is executed every 300000 ms (5 mins). The AlarmReceiver class send a broadcast to my main Activity with the message "SERVICE_TEMPORARY_STOPPED". I already registered that message in my MainActivity via registerReceiver(broadcastReceiver, new IntentFilter("SERVICE_TEMPORARY_STOPPED"));. But, when I add another method, let's say stopAlarm() in the broadcastReceiver, which is going to stop the alarm after 5 mins, the time interval (5 mins) is not applied anymore. In something like 10 secs, it calls the Broadcast Receiver and stop the alarm. And this is the issue. Take a look at the stop() method and how I call it on the broadcastReceiver:
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "I'm running", Toast.LENGTH_SHORT).show();
stopAlarm();
}
};
public void stopAlarm() {
Intent alarmIntent = new Intent(this, AlarmReceiver.class);
pendingIntent = PendingIntent.getBroadcast(this, 0, alarmIntent, 0);
manager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
manager.cancel(pendingIntent);
Log.d(TAG, "Alarm Cancelled");
}
Any clue?
AlarmManager.setRepeating doesn't work properly on different android versions.
Try setExact. It won't repeat but you can achieve repeating functionality as mentioned below:
Updated AlarmReceiver.java
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
context.sendBroadcast(new Intent("SERVICE_TEMPORARY_STOPPED"));
long repeatCount = PreferenceManager.getDefaultSharedPreferences(context).getLong("REPEAT_COUNT", 0L);
repeatCount++;
PreferenceManager.getDefaultSharedPreferences (context).edit().putLong("REPEAT_COUNT", repeatCount).apply()
AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent alarmIntent = new Intent(this, AlarmReceiver.class);
pendingIntent = PendingIntent.getBroadcast(this, 0, alarmIntent, 0);
manager.setExact(AlarmManager.RTC_WAKEUP, (repeatCount *System.currentTimeMillis()),pendingIntent);
}
}
Here we maintain a repeatCount & variable (preference based) and increment it in your AlarmReceiver & schedule alarm again by calculating nextAlarmTime using repeatCount * System.currentTimeMillis();
I have 2 activities, a Main Activity and SetAlarm Activity. I call SetAlarm Activity from Main. When I set up the alarm I create an instance of my main. How do I set up the alarm without creating another instance of the Main or do I kill the main before setting up the alarm? Quite new to this. I have read several of the alarm manager examples, but they all seem to set up a new intent and I think this is what is creating my 2 instances. Is this how you set up the alarm. It does go off.
Here is how I call SetAlarm from the Main:
public void setAlarm(View view) {
Intent intent = new Intent(this, SetAlarmActivity.class);
startActivityForResult(intent, 2);
}
Here is how I set up the Alarm:
public void setUpAlarm() {
if (VERBOSE) Log.v(TAG, "+++ IN setUpAlarm +++");
PLAY_MUSIC = "Y";
Intent intentAlarm = new Intent(this, MainActivity.class);
intentAlarm.putExtra("playMusic",PLAY_MUSIC);
intentAlarm.putExtra("mPos", mPos);
intentAlarm.putExtra("result",ALARM_SET);
setResult(RESULT_OK,intentAlarm);
pIntent = PendingIntent.getActivity(this, 12345,
intentAlarm, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager)(this.getSystemService( Context.ALARM_SERVICE ));
am.set(AlarmManager.RTC_WAKEUP, calSet.getTimeInMillis(), pIntent );
} // setAlarmPlaySong
I cut the alarm off in the main:
#Override
public void onResume() {
if (VERBOSE) Log.v(TAG, "+++ IN onResume +++");
super.onResume();
Intent intent = getIntent()
if (intent.hasExtra("playMusic") && intent.hasExtra("mPos")) {
playMusicFlag = intent.getStringExtra("playMusic");
mPos = intent.getIntExtra("mPos", 0);
if (playMusicFlag.equalsIgnoreCase("Y")) {
if (VERBOSE) Log.v(TAG, "+++ playMusicFlag is SET+++");
playSongs();
showStopAlarmButton();
} // if
}
}
if you want that your startActivity should not start multiple instances of alam activity
you should go to your manifest and have to add an attribute named launchMode for your alarm activity and set it to SingleTop that will ensure that only one instance remains in the taskk back stack(plac where every activity resides in LIFO manner)
In default, an Activity can be instantiated multiple times on multiple tasks. If you want to keep it single, specify android:launchMode="singleTask" on the activity declaration in AnroidManifest.xml and override Activity#onNewIntent() on your main activity to receive a new intent from AlarmManager if main activity is already instantiated.
See Tasks and Back Stack to learn more. You are facing almost same situation shown in Figure 3.
I dont know why you have the SetAlarm Activity, you dont need a activity to set the alarm. Anyways, AlarmManager is a pain to get working. It took me a while to get it up and running. This is what I have in my code now, running.
Calendar cal = Calendar.getInstance();
cal.add(Calendar.SECOND, 5);
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
Intent notifyintent = new Intent(this, OnAlarmReceiver.class);
notifyintent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
notifyintent.setAction("android.intent.action.NOTIFY");
PendingIntent notifysender = PendingIntent.getBroadcast(this, 0, notifyintent,
PendingIntent.FLAG_UPDATE_CURRENT);
am.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 20 * 1000,
notifysender);
OnAlarmReceiver
public class OnAlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// PullPendingRequests.acquireStaticLock(context)
try {
lock = getLock(context);
lock.acquire();
context.startService(new Intent(context, UpdateCustomerRequests.class));
} finally {
if (lock.isHeld()) {
lock.release();
}
}
}
private static final String NAME = "com.commonsware.cwac.wakeful.WakefulIntentService";
private static volatile PowerManager.WakeLock lockStatic = null;
private static PowerManager.WakeLock lock;
synchronized private static PowerManager.WakeLock getLock(Context context) {
if (lockStatic == null) {
PowerManager mgr = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
lockStatic = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, NAME);
lockStatic.setReferenceCounted(true);
}
return (lockStatic);
}
}
IntentService which is called by OnAlarmReceiver
public class UpdateCustomerRequests extends IntentService {
#Override
final protected void onHandleIntent(Intent intent) {
//
//Your stuff here
//
}
public class LocalBinder extends Binder {
public UpdateCustomerRequests getService() {
return UpdateCustomerRequests.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return bindToHomeScreen;
}
}
Android Manifest
Inside manifest tag
<uses-permission android:name="android.permission.WAKE_LOCK" />
Inside application tag
<receiver
android:name="com.taxeeta.support.OnAlarmReceiver"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.NOTIFY" />
</intent-filter>
</receiver>
I am trying to set up an alarm that will run in the background and trigger (eventually) a save event. At the moment I simply have this code attached to a button. Press the button and the alarm should start leaving Toast messages behind as an indication that it is functioning. At the moment everything runs except the onReceive in the BroadcastReceiver is never triggered.
Here is my code:
The class setting up the alarm:
//FIXME - rename (ie BackgroundSave; more descriptive)
public class AlarmReceiver extends Service{
//FIXME - make sure you kill the service
public void onCreate() {
super.onCreate();
Toast.makeText(getApplication().getApplicationContext(), "Service onCreate called", Toast.LENGTH_SHORT).show();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(getApplication().getApplicationContext(), "Service started", Toast.LENGTH_SHORT).show();
setAlarm(AlarmReceiver.this);
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
public void setAlarm(Context c) {
AlarmManager alarmManager = (AlarmManager)c.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(c, Alarm.class);
PendingIntent pi = PendingIntent.getBroadcast(c, 0, i, 0);
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, System.currentTimeMillis() + 1000, 1000, pi);
Toast.makeText(c.getApplicationContext(), "setAlarm called", Toast.LENGTH_SHORT).show();
}
public void cancelAlarm(Context context) {
Intent intent = new Intent(context, Alarm.class);
PendingIntent sender = PendingIntent.getBroadcast(context, 0, intent, 0);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(sender);
}
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
}
Here is the BroadcastReceiver:
public class Alarm extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Alarm", Toast.LENGTH_SHORT).show();
}
}
And here is my manifest:
<!-- Alarm -->
<service android:name="com.xxxx.android.tools.AlarmReceiver" android:enabled="true" />
<receiver android:name="com.xxxx.android.tools.Alarm" ></receiver>
The alarm onReceive is never triggered.
You have to use android.os.SystemClock.elapsedRealtime() as your base time when using AlarmManager.ELAPSED_REALTIME_WAKEUP.
That said i think to use the AlarmManager for your saving purpose is not the best approach. The alarm manager is pretty heavyweight. You should consider using a simple Handler to trigger your save action.
Have a look at Handler.postAtTime().
I'm just trying to get a simple test app working with AlarmManager.
public class TestActivity extends Activity {
private static final int PERIOD = 1000;
private AlarmManager alarmManager;
private PendingIntent pendingIntent;
#Override
public void onCreate(Bundle savedInstanceState) {
Log.v("TextActivity", "WHY NOT!");
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, TestReceiver.class);
pendingIntent = PendingIntent.getBroadcast(this, 0, intent,
PendingIntent.FLAG_CANCEL_CURRENT);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis(), PERIOD, pendingIntent);
Log.v("TestActivity", "Whee!");
}
}
public class TestReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.v("TestReceiver", "Got Here");
}
}
I have tried this on both a hardware phone and the emulator and I am not seeing the messages received. Am I missing something obvious? I am relatively new to Android development.
Did you register your receiver in AndroidManifest.xml? Also period is milliseconds, so that alarm will trigger every second, is this really what you want?