BroadcastReceiver doesn't start activity when app closed - android

So, I've been trying to achieve that for at least five hours today and I've tried literally ANY solution found anywhere.
I'm programming a little clock app, using AlarmManager to make the app ring. It works fine when my app is open or minimized. I'm trying to make it work when the app is closed, and that's the problem. So, there is the piece of code that sets the AlarmManager :
AlarmManager am = (AlarmManager) ctxt.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(ctxt, AlarmService.class);
PendingIntent pen = PendingIntent.getBroadcast(ctxt, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
am.setExact(AlarmManager.RTC_WAKEUP, nextRing.getTime(), pen);
(Here, ctxt is the context, I've tried both getApplicationContext() and getBaseContext() and nextRing.getTime() is a long that represents the date)
Then, I have my AlarmService class (wich used to be a service, which explain the name, but is now a BroadcastReceiver and I just don't want to rename it now)
public class AlarmService extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
/*Intent cust = new Intent(context, CustomIntent.class);
context.startService(cust);*/
Bundle extras = intent.getExtras();
Intent newIntent = new Intent(context, AlarmActivity.class);
newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
newIntent.putExtra("AlarmName", extras.getString("AlarmName"));
context.startActivity(newIntent);
}
}
So this is the try with only the BroadcastReceiver, wich doesn't work obviously, so I tried to add a IntentService (commented out code at the top) which has the following code
public class CustomIntent extends IntentService {
public CustomIntent() {
super("CustomIntent");
}
#Override
protected void onHandleIntent(Intent intent) {
Intent newIntent = new Intent(getBaseContext(), AlarmActivity.class);
newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getApplication().startActivity(newIntent);
}
}
... and that's not working either ! Finally, here's the manifest I use :
<application>
[Here goes stuff that have nothing to do with my bug]
<receiver android:name=".custom_classes.AlarmService"/>
<service
android:name="com.group9.abclock.custom_classes.CustomIntent"
android:enabled="true" >
<intent-filter>
<action android:name="com.group9.abclock.custom_classes.CustomIntent" />
</intent-filter>
</service>
</application>
Sorry for the (very) long post but I tough I should explain anything I tried. Thanks in advance if you can help !

If your application is closed, you can't trigger a BroadcastReceiver because it's not registered, and you can't use Context methods because there is no Context.
If you want to execute tasks while the application is closed, you have to create another project with a Service, and start it with your application.
A Service runs in background until someone kill it, and once it's started, the main application is not needed anymore. So the ring functionality have to be implemented in this service.
Just remember that the AlarmManager.setExact, is not that exact from API 23, due to Doze Mode.

Related

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

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.

AlarmManager Stops after removing app from recents apps

I am new to this part of android, and here I aim to use alarm manager to run a code snippet every 2 minute which will poll a server (using the website's api) and based on the returned JSON generate notification.
After a looking up the web I thought one of the best option in my case will be using intent service and android.
Manifest of Services and Recievers
<service
android:name=".NotifyService"
android:enabled="true"
android:exported="false" >
</service>
<receiver
android:name=".TheReceiver"
android:enabled="true"
android:exported="true" >
</receiver>
<receiver
android:name=".OnOffReceiver"
android:enabled="true"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
Part in the flash screen activity where I call the intent service which is responsible for polling for notification:
Intent msgIntent = new Intent(this, NotifyService.class);
startService(msgIntent);
The receiver to start the alarm on device start:
public class OnOffReceiver extends BroadcastReceiver
{
private AlarmManager alarmMgr;
private PendingIntent alarmIntent;
public OnOffReceiver(){}
#Override
public void onReceive(Context context, Intent intent)
{
Intent service = new Intent(context, NotifyService.class);
service.setAction(NotifyService.CREATE);
context.startService(service);
}
}
The IntentService Class
public class NotifyService extends IntentService
{
public NotifyService()
{
super("NotifyService");
}
public static final int STATUS_RUNNING = 0;
public static final int STATUS_FINISHED = 1;
public static final int STATUS_ERROR = 2;
#Override
protected void onHandleIntent(Intent intent)
{
if (intent != null)
{
final String action = intent.getAction();
}
StartStuff();
}
public void StartStuff()
{
Intent intent = new Intent(this, TheReceiver.class);
PendingIntent pend_intent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,1200,1200, pend_intent);
//1200ms to make it easier to test
}
}
The receiver class which sets notification, for testing pupose I am not doing any network related work here just making a simple notification to check if the app is running in all situations
public class TheReceiver extends BroadcastReceiver
{
public TheReceiver(){}
#Override
public void onReceive(Context context, Intent intent)
{
Toast.makeText(context, " Success ", Toast.LENGTH_SHORT).show();
Log.d("Notification", "The Receiver Successful");
showNotification(context);
}
private void showNotification(Context context)
{
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(context).setContentTitle("My notification").setContentText("Hello World!");
mBuilder.setDefaults(Notification.DEFAULT_SOUND);
mBuilder.setAutoCancel(true);
NotificationManager mNotificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(1, mBuilder.build());
}
}
However the notification come only when the app is running or in the recent apps tray.
It does not start notifying when the phone reboots, nor does it notify after the app is removes from the recent apps tray.
The app needs Notify users like other apps (like gmail, whatsapp) do, even if they are swiped out of the recent apps tray.
Timeliness and punctuality are not very big issue as delay up to 5 to 10 minutes are tolerable. (I intend to poll ever 2 minutes though.)
Where am I going wrong? Also, is there a better way to go about the problem?
To keep a receiver active after closing the app is to use
android:process=":remote"
in the manifest file for the receiver that needs to be kept alive.
<receiver
android:name=".TheAlarmReceiver"
android:process=":remote">
</receiver>
in the manifest for the receiver (TheReceiver in this case) that we need to keep active after the app closes.
P.S. : I also changed the way I use IntentsService and AlarmManager for the application, as my previous(above) implementation is not a very good way to go around it.
If an App is killed from recent apps or from "force stop" it won't restart by itself. The user has to start the app again in order to make it run again. There is no way to prevent this. It's just the way android works.
However there is a way to make your app run oon boot. Check out this link.

WakefulBroadcastReceiver intent not starting only in some cases (strange)

I have a very strange bug happening in my app. I am building an Alarm APP and I am using SQLite to store alarm data and broadcast receivers to manage the alarm manager calls.
The code of onReceive strangely does not behave in the same way in some cases. I try to start an Activity when the receiver receives a broadcast, nearly 90% of cases everything goes well and I manage to start the Activity, but in some cases strangely enough the receiver executes the instruction "startActivity(i)" but nothing happens.
It is really hard to reproduce the BUG and during my debugging I have learned what I have mentioned, but more than this is really difficult for me to understand how a call to startActivity() in most cases works and in some cases does not work. I have searched through the Stack community but no one seamed to have this kind of problem, everybody just had problems in starting the activity because they had not set the flag or because they had not registered the receiver in the manifest. Below I am posting the code.
public class AlarmReceiver extends WakefulBroadcastReceiver {
// The app's AlarmManager, which provides access to the system alarm services.
private AlarmManager alarmMgr;
// The pending intent that is triggered when the alarm fires.
private PendingIntent alarmIntent;
#Override
public void onReceive(Context context, Intent intent) {
Utils.logToFile("Received Alarm ,I am in onReceive(), ALARM ID: "+intent.getExtras().getInt(Constants.ALARM_ID));
Intent intent = new Intent(context, StopAlarm.class);
Bundle b = new Bundle();
b.putInt(Constants.ALARM_ID, intent.getExtras().getInt(Constants.ALARM_ID));
if(intent.getExtras().containsKey(Constants.SNOOZE_ALARM)){
b.putString(Constants.SNOOZE_ALARM, intent.getExtras().getString(Constants.SNOOZE_ALARM));
}
i.putExtras(b);
//this flag is needed to start an Activity from a BroadcastReceiver
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
//this method reads from the DB and sets the next alarm
//I tried commenting this method so that no DB action is
//performed and still the bug happened
setAlarm(context.getApplicationContext());
//this method just logs data into a file that I have created to keep track of events
//since not always the device is connected with LogCat
Utils.logToFile("Received Alarm, Intent(context, StopAlarm.class);");
}
Do I need to set any other flag and how is it possible that startActivity(intent) behaves incorrectly in some cases?
EDIT
<activity
android:label="#string/app_name"
android:name="package.activity.StopAlarm"
android:windowSoftInputMode="stateAlwaysHidden"
android:screenOrientation="sensorPortrait">
</activity>
<receiver android:name="package.receivers.AlarmReceiver" />
I have finally solved the issue by creating an IntentService and by starting the activity from the IntentService and setting two flags to the Intent. After doing this I placed the code which reads from DB in the activity that is started from the IntentService. I have tested nearly 60 times the behaviour and in all the tests the app behaved correctly. I am posting the code below.
public class MyAlarmReceiver extends WakefulBroadcastReceiver {
// The app's AlarmManager, which provides access to the system alarm services.
private static AlarmManager alarmMgr;
// The pending intent that is triggered when the alarm fires.
private static PendingIntent alarmIntent;
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, AlarmIntentService.class);
Bundle b = new Bundle();
b.putInt(Constants.ALARM_ID, intent.getExtras().getInt(Constants.ALARM_ID));
if(intent.getExtras().containsKey(Constants.SNOOZE_ALARM)){
b.putString(Constants.SNOOZE_ALARM, intent.getExtras().getString(Constants.SNOOZE_ALARM));
}
i.putExtras(b);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startWakefulService(context, i);
}
This is the IntentService I needed to implement
public class AlarmIntentService extends IntentService {
public AlarmIntentService() {
super("AlarmIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
Intent i = new Intent(this, StopAlarm.class);
Bundle b = new Bundle();
b.putInt(Constants.ALARM_ID, intent.getExtras().getInt(Constants.ALARM_ID));
if(intent.getExtras().containsKey(Constants.SNOOZE_ALARM)){
b.putString(Constants.SNOOZE_ALARM, intent.getExtras().getString(Constants.SNOOZE_ALARM));
}
i.putExtras(b);
//THESE ARE THE FLAGS NEEDED TO START THE ACTIVITY AND TO PREVENT THE BUG
//(CLEAR_TASK is crucial for the bug and new task is needed to start activity from outside of an activity)
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(i);
MyAlarmReceiver.completeWakefulIntent(intent);
}
}
This is the activity started by the IntentService. Here I set the next alarm.
public class StopAlarm extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.stop_alarm);
//this method reads from the DB and sets the next alarm
MyAlarmReceiver.setAlarm(getApplicationContext());
...
I had a similar issue. From my experience the problem is,that startActivity(i) is asynchronous. So in this case the program will simultaneously call activities onCreate(),onStart(), etc. but also call QlokAlarmReceiver.completeWakefulIntent(intent) (without waiting for the activity to be closed), which will release the wakeLock. Because of that the device can go to sleep during executing the activities onCreate() or onStart().
Rubin, I know, that my answer is a contradiction of your solution, but my logs clearly indicated such order of events:
- startActivity called
- onCreate of the activity called
- completeWakefulIntent(intent); called in between logs from onStart of the activity
My workaround this is to start a wakelock with a timeout of eg 20 seconds just before calling startActivity and then start another wakeLock in the activities onCreate, which will be released in the onDestroy method.
I'm not sure if my solution goes along with best practises, but I haven't found a better solution so far.

How do you start an Activity with AlarmManager in Android?

I've poured through a dozen tutorials and forum answers about this problem, but still haven't been able to get some working code together. I'll try to keep the question straightforward:
How do you use AlarmManager (in the Android API) to start an Activity at a given time? Any solution to this problem will do.
My latest attempt to achieve this is below.
(Imports omitted. I expect MyActivity to start 3 seconds after the program is opened, which it doesn't. There are no error messages to speak of.)
public class AndroidTest2Activity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Context context = this;//.getApplicationContext();
AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); // CORRECT
Intent intent = new Intent(context, myReceiver.class); // CORRECT
PendingIntent pending = PendingIntent.getBroadcast( context, 0, intent, 0 ); // CORRECT
manager.set( AlarmManager.RTC, System.currentTimeMillis() + 3000, pending ); // CORRECT
setContentView(R.layout.main);
}
}
public class myReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
Intent i=new Intent(context, myActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
public class myActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("", "Elusive success");
setContentView(R.layout.main);
}
}
Any advice would be appreciated.
Please note: I've got myReceiver in the manifest already
In case someone else stumbles upon this - here's some working code (Tested on 2.3.3 emulator):
public final void setAlarm(int seconds) {
// create the pending intent
Intent intent = new Intent(MainActivity.this, AlarmReceiver.class);
// intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
pendingIntent = PendingIntent.getBroadcast(MainActivity.this, 0,
intent, 0);
// get the alarm manager, and scedule an alarm that calls the receiver
((AlarmManager) getSystemService(ALARM_SERVICE)).set(
AlarmManager.RTC, System.currentTimeMillis() + seconds
* 1000, pendingIntent);
Toast.makeText(MainActivity.this, "Timer set to " + seconds + " seconds.",
Toast.LENGTH_SHORT).show();
}
public static class AlarmReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
Log.d("-", "Receiver3");
}
}
AndroidManifest.xml:
<receiver android:name="com.example.test.MainActivity$AlarmReceiver" >
</receiver>
Issues with BenLambell's code :
EITHER:
Move the receiver to it's own .java file or
make the inner class static - so it can be accessed from outside
Receiver is not declared correctly in the manifest:
if it's an inner class in MainActivity use:
<receiver android:name="package.name.MainActivity$AlarmReceiver" ></receiver>
if it's in a separate file:
<receiver android:name="package.name.AlarmReceiver" ></receiver>
If your intention is to display a dialog in the receiver's onReceive (like me): that's not allowed - only activities can start dialogs. This can be achieved with a dialog activity.
You can directly call an activity with the AlarmManager:
Intent intent = new Intent(MainActivity.this, TriggeredActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(MainActivity.this, 0, intent, PendingIntent.FLAG_ONE_SHOT);
((AlarmManager) getSystemService(ALARM_SERVICE)).set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + seconds * 1000, pendingIntent);
How do you use AlarmManager (in the Android API) to start an Activity at a given time?
Supply a PendingIntent to the set() call that identifies the activity to start up. Or, do what you're doing, which should work just fine.
This sample project is a bit elaborate, because it's 19 tutorials deep into one of my books, but if you look at classes like EditPreferences, OnBootReceiver, and OnAlarmReceiver, you will see the same basic recipe that you're using above. In this case, I could have just used a getActivity() PendingIntent, but the tutorial after this one gives the user a choice of launching an activity or displaying a Notification, so a BroadcastReceiver makes more sense.
Look for warnings in addition to errors in LogCat. Most likely, your receiver or activity is not in your manifest.
Note that popping up an activity out of the middle of nowhere is generally not a good idea. Quoting myself from the book in question:
Displaying the lunchtime alarm via a full-screen activity certainly works,
and if the user is looking at the screen, it will get their attention. However,
it is also rather disruptive if they happen to be using the phone right that
instant. For example, if they are typing a text message while driving, your
alarm activity popping up out of nowhere might distract them enough to
cause an accident. So, in the interest of public safety, we should give the user an option to
have a more subtle way to remind them to have lunch.
add this in your android mainifest file and it will hopefully work
<activity android:name=".MyReceiver" />
<receiver android:name=".MyReceiver"> </receiver>
In my experience you can achieve this without broadcast receiver, just use PendingIntent.getActivity() instead of getbroadcast()
private void setReminder(){
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Calendar startTime = Calendar.getInstance();
startTime.add(Calendar.MINUTE, 1);
Intent intent = new Intent(ReminderActivity.this, ReminderActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(ReminderActivity.this, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.set(AlarmManager.RTC, startTime.getTimeInMillis(), pendingIntent);
}
I've tested this code on android O but I'm not sure about other android versions please inform me if this doesn't work on any other android version.
Main Problem : if you close completely you're app and expect to start you're activity after 3 seconds, you wrong. because when you close you're app , you're app cant receive broadcast, for solve this problem use services instead of broadcasts.
Point: when you're service would ran ,you cant start your activity if your app wouldn't in foreground.
Solution: I think when your service started you can again set Alarmmanager to start your activity with PendingIntent for just now.
Remember :
When you create your intent for pass it to pendingIntent add the FLAG_ACTIVITY_NEW_TASK to it.
For this PendingIntent use PendingIntent.getActivity() method and for the first PendingIntent use PendingIntent.getService() method.
I hope this help you.
I had this problem too long ago to know which answer is correct, but thank you to everyone for their responses. I'm self-answering so the question isn't still open.
According to Java convention class name begin with Capital letter.So change your
"myReceiver" to "MyReceiver" and "myActivity" to "MyActivity".
Then add your receiver in the manifest file like the below.
<application
------------
<receiver android:name="MyReceiver"></receiver>
---------------------
</application>
you are not sending any broadcast for the receiver to receiver and further more it lokks like u want a splash screen or something like that for that purpose u can start a new thread wait for some sec then start ur activity in that and for that time period u can do what ever u want on the UI thread ...

Categories

Resources