I have a service which is called with
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
getActivity().startForegroundService(new Intent(getActivity(),
Background.class));
} else {
getActivity().startService(new Intent(getActivity(), Background.class));
}
and the service it's self being
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this,"Creating Notification",Toast.LENGTH_SHORT).show();
//
initChannels(this);
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, "default")
.setContentTitle("Zeep!?")
.setTicker("Zeep!?")
.setContentText("We're currently working in the background")
.setSmallIcon(R.mipmap.zeep_icon_b)
.setOngoing(true)
.setPriority(Notification.PRIORITY_MIN)
.setContentIntent(pendingIntent)
.build();
startForeground(1337, notification);
//
return START_NOT_STICKY;
}
but whenever I start the app and the close the app, it crashes and causes the phone to soft reboot, i'm so confused by it all, Thanks
My onStartCommand() looks like this:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Tells the system to not try to recreate the service after it has been killed.
return START_NOT_STICKY;
}
I take care of the notification stuff in onCreate() instead. Also, you need to call startForeground() immediately after calling startForegroundService():
#Override
public void onCreate() {
mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
// Android O requires a Notification Channel.
if (Build.VERSION.SDK_INT >= 26) {
CharSequence name = getString(R.string.app_name);
// Create the channel for the notification
#SuppressLint("WrongConstant")
NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, name, NotificationManager.IMPORTANCE_LOW);
// Set the Notification Channel for the Notification Manager.
if (notificationManager != null) {
notificationManager.createNotificationChannel(mChannel);
}
//Since MainActivity binds with the service and calls onCreate, we can actually call startForegroundService from within the service itself.
startForegroundService(new Intent(ForegroundService.this, ForegroundService.class));
//We only need to call this for SDK 26+, since startForeground always has to be called after startForegroundService.
startForeground(NOTIFICATION_ID, getNotification());
}
else {
//Since MainActivity binds with the service and calls onCreate, we can actually call startService from within the service itself.
startService(new Intent(ForegroundService.this, ForegroundService.class));
}
Not saying this is the solution, but it works for me.
START_NOT_STICKY
If the system kills the service after onStartCommand() returns, do not recreate the service unless there are pending intents to deliver. This is the safest option to avoid running your service when not necessary and when your application can simply restart any unfinished jobs.
START_STICKY
If the system kills the service after onStartCommand() returns, recreate the service and call onStartCommand(), but do not redeliver the last intent. Instead, the system calls onStartCommand() with a null intent unless there are pending intents to start the service. In that case, those intents are delivered. This is suitable for media players (or similar services) that are not executing commands but are running indefinitely and waiting for a job.
START_REDELIVER_INTENT
If the system kills the service after onStartCommand() returns, recreate the service and call onStartCommand() with the last intent that was delivered to the service. Any pending intents are delivered in turn. This is suitable for services that are actively performing a job that should be immediately resumed, such as downloading a file.
You can use START_NOT_STICKY but then you will have to manually handle the stopping of the service.
Also remember that when you are calling a service from an activity the onCreate() doesn’t always gets called. Only when you are calling a service from a non activity it gets called else the onStartCommand() gets called.
I think this library has the best service implementation for android. Check it out MockGeoFix.
Related
I'm beginner android programmer. While experimenting with background Services and background data download I came across this particular problem:
I'm using an AlarmManager to schedule repeating background data download.
public void setBackgroundDataService(Context context, long time_in_millis) {
Intent background_service_intent = new Intent(context, BackgroundDataService.class);
background_service_intent.putExtra("value", val);
PendingIntent pending_intent = PendingIntent.getService(context, BACKGROUND_DATA_SERVICE_ID , background_service_intent, PendingIntent.FLAG_UPDATE_CURRENT);
cancelBackgroundServiceAlarmIfExists(context, background_service_intent);
AlarmManager alarm_manager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
alarm_manager.setRepeating(AlarmManager.RTC, (System.currentTimeMillis() + time_in_millis), time_in_millis, pending_intent);
}
public static void cancelBackgroundServiceAlarmIfExists(Context context, Intent intent) {
// try to cancel Pending Intent if exists
try {
PendingIntent pending_intent = PendingIntent.getService(context, BACKGROUND_DATA_SERVICE_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarm_manager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
alarm_manager.cancel(pending_intent);
Log.d("DEBUG", "Background service terminated");
} catch (Exception e) {
e.printStackTrace();
}
}
Here is onStartCommand() from Service:
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("DEBUG", "Background service invoked");
if (intent == null)
return super.onStartCommand(intent, flags, startId);
Bundle extras = intent.getExtras();
some_value = extras.getString("value");
startDataDownload();
return super.onStartCommand(intent, flags, startId);
}
Data download is performed in AsyncTask
So the problem is:
Why after swiping the app from applications tray Service gets restarted and intent passed in arguments is null. Proper working of the Service relies on extras passed in the intent. At first my application would just crash (usually 2 times, I assume that something was trying to restart it, but crashed while trying to get extras from null intent). After some time Service would start properly and do its job without crashing (probably AlarmManager restarted it with proper intent extras after specified time). Then sporadically it would crash again. I managed to avoid crashes by checking whether intent is null or not. It seems to work. But the question remains.
Why is my Service getting restarted and null intent gets passed to it right after I swipe my app from application tray. Is there anything thet can be done about it? Is there a better way to download data periodically?
In onStartCommand() you are returning START_STICKY because of this:
return super.onStartCommand(intent, flags, startId);
START_STICKY tells Android that it should restart your Service if the Service is killed (for whatever reason). When you swipe your app from the list of recent tasks, Android kills the OS process hosting your app and then, because your Service returned START_STICKY, Android restarts your Service.
After restarting your Service, Android calls onStartCommand() with a null Intent parameter.
It looks like you are also not stopping your Service after the data download completes.
If you only want your Service to be restarted if it is killed while it is working on something, return START_REDELIVER_INTENT from onStartCommand().
The documentation for Service explains this:
For started services, there are two additional major modes of
operation they can decide to run in, depending on the value they
return from onStartCommand(): START_STICKY is used for services that
are explicitly started and stopped as needed, while START_NOT_STICKY
or START_REDELIVER_INTENT are used for services that should only
remain running while processing any commands sent to them.
I've faced this kind of problem recently. So what I did is make my service looks like a foreground task and that'll solve your problem. And to do that the best approach I can think of is using a notification. Here is the required documentation. And here is a sample code for the notification -
private void showNotification() {
String notificationTitle = "Notification Title";
String notificationContent = "Notification Content";
Intent notificationIntent = new Intent(getApplicationContext(), DashboardActivity.class)
.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0,
notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(context, channelId)
.setTicker(AppConstants.App_Name)
.setSmallIcon(R.drawable.logo)
.setContentTitle(notificationTitle)
.setColor(getApplicationContext().getResources().getColor(R.color.colorWhite))
.setStyle(new NotificationCompat.BigTextStyle().bigText(notificationContent))
.setAutoCancel(false)
.setOngoing(true)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
}
If you like to stop this process just call void stopForeground (int flags) where ever you like. It won't kill your service but your process will no longer be a foreground task.
I've created a service , And I also created a notification so when my service runs there is a notification for it.
Now , I want users to be able to swipe/dismiss the notification but when trying to do so I've encountered two problems:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
password = intent.getStringExtra("password");
number = intent.getStringExtra("number");
Intent activityIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0,
activityIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new Notification.Builder(this).
setContentTitle(getText(R.string.app_name)).
setContentText("Subject").
setContentInfo("Doing stuff in the background...").
setSmallIcon(R.drawable.ic).
setAutoCancel(true).
setContentIntent(pendingIntent).build();
startForeground(1, notification);
return super.onStartCommand(intent, flags, startId);
}
This code worked perfectly , the only problem with this code is that users cannot swipe dismiss the notification , So from searching around I found that I can fix it by replacing 'startForeground' function with
NotificationManager notificationManager =
(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(0,notification);
And then it worked, I can swipe dismiss, But now I get a different problem , Once I close my application (using long press on middle button then close all applications)
My app thrrows a nullpointerexception few moments later pointing to the line :
password = intent.getStringExtra("password");
as if the intent is null. This does not happen when I use the startForeground function
What might be the problem ?
Return START_NOT_STICKY in onStartCommand().
There are three options to what should happen once the proccess in which the service is running on crushes:
START_STICKY , START_NOT_STICKY , START_REDELIVER_INTENT.
I wanted to reload the service and keep the intent once it crushes.
START_REDELIVER_INTENT did the trick, it restarts the service and redelivers the intent.
START_STICKY , Almost did the trick , the problem with START_STICKY for me was that it restarts the service but with a null intent.
AutoCancel does not work when service is still on foreground. Try remove service from foreground first:
startForeground(1, notification);
stopForeground(false); //false - do not remove generated notification
I developed an app which should remind me at a certain time. Therefore I implemented an IntentService which starts a notification. The problem is that the notification will be created while the app is in foreground or at least in background open. If I close the app using the task manager the notification is no longer running.
Am I using the wrong service? Or do I have to create something else?
in the intentservice class:
private static final String serviceName = "sebspr.de.deadlines.DeadLineService";
public DeadLineService() {
super(serviceName);
}
#Override
protected void onHandleIntent(Intent intent) {
Context ctx = getApplicationContext();
Intent intent = new Intent(ctx, DeadLineService.class);
PendingIntent pIntent = PendingIntent.getActivity(ctx, 0, intent, 0);
String title = getTitle(list.size());
String text = getText(list);
Notification noti = new Notification.Builder(ctx)
.setContentTitle(title)
.setContentText(text).setSmallIcon(R.mipmap.ic_notfi)
.setContentIntent(pIntent)
.build();
NotificationManager notificationManager = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);
// hide the notification after its selected
noti.flags |= Notification.FLAG_AUTO_CANCEL;
notificationManager.notify(0, noti);
}
int the MainActivity:
Intent msgIntent = new Intent(this, DeadLineService.class);
startService(msgIntent);
UPDATE
Considering my actual problem the solution is, to take a look at the AlarmManager. The IntentService is here the wrong way to go. I found a good tutorial here: AlarmManger Example
You shouldn't worry about a user killing the app via the task manager. If that happens, you really shouldn't start back up automatically. It isn't a user-friendly way to do things.
What you should worry about is a system restart in which case you can register your app to be notified when the device turns on.
Check out this answer for more details on starting your service when the phone turns on.
Also start your service sticky
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
Actually u can try to keep your application alive after killed but it may annoy your users a bit... So, Look out...
How to save Alarm after app killing?
I'm trying to implement a "daily reminder" function in my Android app, that should fire once per day at a set time. The first implementation I tried worked for most people, but some subset of users (including at least one person running on Android 4.3 on a Samsung) were reporting that the alarm was firing WAY more often than it should, e.g. every 10 minutes, and every time they opened the app, and just generally being very annoying.
Here's how the alarm is enabled:
Intent myIntent = new Intent(ctx, AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(ctx, 0, myIntent,0);
AlarmManager alarmManager = (AlarmManager)ctx.getSystemService(Service.ALARM_SERVICE);
alarmManager.cancel(pendingIntent);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, sched,
AlarmManager.INTERVAL_DAY,
pendingIntent);
Then there's this AlarmReceiver class:
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Intent service1 = new Intent(context, AlarmService.class);
context.startService(service1);
}
}
This is registered as a receiver in the AndroidManifest: <receiver android:name=".AlarmReceiver"/>
Finally there's the AlarmService, which used to look like this:
public class AlarmService extends Service {
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate()
{
// TODO Auto-generated method stub
super.onCreate();
}
#SuppressWarnings("static-access")
#Override
public void onStart(Intent intent, int startId)
{
super.onStart(intent, startId);
Log.v("pm", "about to notify");
Intent intent1 = new Intent(this.getApplicationContext(), MainActivity.class);
intent1.setAction(Intent.ACTION_MAIN);
intent1.addCategory(Intent.CATEGORY_LAUNCHER);
//intent1.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP| Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingNotificationIntent = PendingIntent.getActivity( this.getApplicationContext(),0, intent1,PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new Notification.Builder(this.getApplicationContext())
.setContentTitle("My App")
.setContentText("Don't forget that thing!")
.setSmallIcon(R.drawable.ic_launcher)
.setWhen(System.currentTimeMillis())
.setContentIntent(pendingNotificationIntent)
.getNotification();
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.defaults |= Notification.DEFAULT_SOUND;
notification.defaults |= Notification.DEFAULT_VIBRATE;
NotificationManager nManager =
(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nManager.notify(0, notification);
}
#Override
public void onDestroy()
{
// TODO Auto-generated method stub
super.onDestroy();
}
}
However, as I say, people were reporting that this fired every ten minutes or so! So I tried changing the AlarmService to a less deprecated implementation, but in the process now people are saying it only fires once, and then never again!
I replaced onStart with this:
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
Log.v("pm", "about to notify");
if (intent != null) {
Intent intent1 = new Intent(this.getApplicationContext(), MainActivity.class);
intent1.setAction(Intent.ACTION_MAIN);
intent1.addCategory(Intent.CATEGORY_LAUNCHER);
//intent1.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP| Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingNotificationIntent = PendingIntent.getActivity( this.getApplicationContext(),0, intent1,PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new Notification.Builder(this.getApplicationContext())
.setContentTitle("My App")
.setContentText("Don't forget that thing!")
.setSmallIcon(R.drawable.ic_launcher)
.setWhen(System.currentTimeMillis())
.setContentIntent(pendingNotificationIntent)
.getNotification();
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.defaults |= Notification.DEFAULT_SOUND;
notification.defaults |= Notification.DEFAULT_VIBRATE;
NotificationManager nManager =
(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nManager.notify(0, notification);
} else {
Log.v("pm", "Null Intent");
}
return START_STICKY;
}
Since I can't reproduce the original issue on my devices, it's a bit hard to test! My two theories are:
The problem lies in AlarmReceiver, like it ought to not be starting up a brand new service but doing something with the existing service
I shouldn't bother excluding null intent values in my onStartCommand function
I'm just a little nervous to try number 2 in case it causes people's devices to annoy them again!
Here's how the alarm is enabled
Note that this will be inexact on Android 4.4+, even though you are using setRepeating(), once you raise your android:targetSdkVersion to 19 or higher.
Then there's this AlarmReceiver class
That will not be reliable with a _WAKEUP-style alarm. It is eminently possible for the device to fall asleep between the startService() call and when your service actually gets a chance to do something. Please use WakefulBroadcastReceiver or my WakefulIntentService for _WAKEUP-style alarms if you are going to use the delegate-to-a-service pattern.
but in the process now people are saying it only fires once per day!
Since that is what you want, I would think that this is a good thing.
I replaced onStart with this:
I do not know why you are using a Service instead IntentService. Regardless, please call stopSelf() at the bottom of your onStartCommand() method, so the service goes away. There is no reason for this service to stay running once this work is completed. Also, replace START_STICKY with START_NOT_STICKY.
And, if this is all the work you intend to do in the service, you could dump the service entirely and move your onStartCommand() guts into onReceive() of the BroadcastReceiver.
The pattern of delegating work to a service from a receiver is used when the work will take too long to risk tying up the main application thread (e.g., >1ms)... but then your service needs a background thread, which yours lacks. Since I would expect your code to be less than 1ms in execution time, you could just do that in onReceive() and simplify your app, you would no longer need the separate Service, nor any of the Wakeful* stuff I mentioned earlier.
The problem lies in AlarmReceiver, like it ought to not be starting up a brand new service but doing something with the existing service
If this only runs once per day, there better not be an "existing service". There is no need for you to have a running process, tying up system RAM, just waiting for the clock to tick.
I shouldn't bother excluding null intent values in my onStartCommand function
You will get a null Intent if:
Your process was terminated before the service completed onStartCommand() for a startService() call, and
Your service had successfully run onStartCommand() before and returned START_STICKY
I'm more and more wondering why my AlarmReceiver creates a service, rather than just showing the notification directly.
Agreed. If you plan on lots more work, involving disk or network I/O, then use an IntentService (background thread, service stops itself automatically). Otherwise, I'd just put this in onReceive() and call it good.
I just wanted to add for all people who got problems with the AlarmManager and Android 4.4+ that it is truly important that you add the stopSelf();, like #CommonsWave already said, to the bottom of your onStartCommand() within Service which is called from the BroadcastReceiver
This is onStartCommand()
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Notification creation code
telMgr = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
telMgr.listen(new PSL(), PhoneStateListener.LISTEN_CALL_STATE);
return super.onStartCommand(intent, flags, startId);
}
and PhoneStateListener class (under service class)
public class PSL extends PhoneStateListener {
public void onCallStateChanged(int state, String incomingNum) {
switch(state) {
case TelephonyManager.CALL_STATE_IDLE:
case TelephonyManager.CALL_STATE_OFFHOOK:
//Work1
break;
case TelephonyManager.CALL_STATE_RINGING:
//Work2
break;
}
}
}
both of them are in same .java file
I have these code on one service class.
when I call startService() from main activity, it works well.
but when my app is killed by Task Manager, Killer or automatically by shortage of memory on device, Service does restarts but not working.
when i go to Setting - Application - Running, it shows process 1 and service 1, before/after killed.but after killed, memory share goes 1/10. I have tried startForeground() not to be killed easily with my notification - it didn't work. (doesn't show any notification)
and also tried return of onStartCommand(): START_STICKY, START_REDELIVER_INTENT - shows same result is there any way I can restart completely or make it not killed?
After Spending few Hours I fount that , For Android 2.0 or later you can use the startForeground() method to start your Service in the foreground.
Documentation provided by Android
A started service can use the startForeground(int, Notification) API to put the service in a foreground state, where the system considers it to be something the user is actively aware of and thus not a candidate for killing when low on memory. (It is still theoretically possible for the service to be killed under extreme memory pressure from the current foreground application, but in practice this should not be a concern.)
Ther are very rare chances of Foreground Service to kill by OS.But It works fine.
public class ServicePhoneState extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("Music Player")
.setTicker("Google Music Player")
.setContentText("My Music")
.setSmallIcon(R.drawable.ic_bg_icon)
.setPriority(Notification.PRIORITY_MAX)
.setContentIntent(pendingIntent)
.setOngoing(false)
.build();
startForeground(10, notification);
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
stopForeground(true);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
I solved it by startForeground()
It didn't work because I had not used Notification.FLAG_FOREGROUND_SERVICE on Notification
After Spending few Hours I fount that , For Android 2.0 or later you can use the startForeground() method to start your Service in the foreground.
Documentation provided by Android
A started service can use the startForeground(int, Notification) API to put the service in a foreground state, where the system considers it to be something the user is actively aware of and thus not a candidate for killing when low on memory. (It is still theoretically possible for the service to be killed under extreme memory pressure from the current foreground application, but in practice this should not be a concern.) Ther are very rare chances of Foreground Service to kill by OS.But It works fine.
public class ServicePhoneState extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
MyPhoneStateListener phoneListener = new
MyPhoneStateListener(ServicePhoneState.this);
TelephonyManager telephony = (TelephonyManager)
getSystemService(Context.TELEPHONY_SERVICE);
telephony.listen(phoneListener, PhoneStateListener.LISTEN_CALL_STATE);
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("Music Player")
.setTicker("Google Music Player")
.setContentText("My Music")
.setSmallIcon(R.drawable.ic_bg_icon)
.setPriority(Notification.PRIORITY_MAX)
.setContentIntent(pendingIntent)
.setOngoing(false)
.build();
startForeground(10, notification);
return START_NOT_STICKY;}
#Override
public void onDestroy() {
super.onDestroy();
stopForeground(true);
}
#Override
public IBinder onBind(Intent intent) {
return null;}}