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
Related
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.
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 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 am implementing an app kind of an app tracker which requires to run the service all the time in background. So the service is called when application is opened and it stops when stopself() is called. Service also consists of a thread which runs all the time. It used to run perfect. But from last few days it stops after sometime. When coming to my app's ui after some task the service stops!
Can anyone suggest me any solution?
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e("SERVICE","started");
if(intent != null && intent.getAction() != null)
{
day=intent.getExtras().getInt("day")+5;
Toast.makeText(this, "day" +day, Toast.LENGTH_LONG).show();
apps=intent.getStringArrayListExtra("apps");
items=apps.size();
timer=intent.getIntegerArrayListExtra("timer");
Run[] a=new Run[items];
t=new Thread[items];
for(int i=0;i<items;i++)
{
int sec=0;
app=apps.get(i).toString();
time=timer.get(i);
a[i]=new Run(app, time,sec);
Log.e("APPTIME",app+ " "+time);
t[i]=new Thread(a[i]);
t[i].start();
}
}
return super.onStartCommand(intent, flags, startId);
}
You should start your service as STICKY_SERVICE
Example for starting as STICKY
Thread for START_STICKY and START_NOT_STICKY
You should consider reading this, if you are working on Android KitKat
Try implementing foreground service. foreground service
Foreground service displays notification and is never stopped until you want.
Implement this code snippet in your service's onCreate
Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, getText(R.string.notification_title),
getText(R.string.notification_message), pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID, notification);
So I've created a service and it works great. All it does is just count. I have a custom class that handles the hassle of using notifications. This class has a getNotification which, obviously, returns the notification it uses. Everything worked great but I've got to make my service to run on foreground (it syncs some important data to the app which must not be interrupted until it finishes) Right now when I'm adding startForeground I add my notification and an id which leaves it like this.
startForeground(1337, notification);
Problem I have is that the first notify() I do is, for some reason, independent from the others notifications. So when I make this run it creates two notifications. The first one is stuck on the first update which has a title called "Zilean" and it's content says "Counting". The other one updates perfectly. I've notice that if startForeground is ran with an id of 0 (startForeground(0,notification)) then this problem gets fixed, but if I kill the activity the notification dies. Doesn't happen when the id <> 0.
I had this problem for too long so I'm using a dummy service which only counts. I want to know that if the activity dies due to the user or just because android decided to delete it's memories, then the service will keep going.
// onStartCommand
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// NOTIFICATION SECTION
NotificationManager mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
this);
notificationManagerClass = new SBNNotification(mNotifyManager,
mBuilder, true, 1, false, "Zilean",
"Initiating Counter", false);
notificationManagerClass.notificate();
final Notification notification = notificationManagerClass.getNotification();
notification.flags = flags;
notification.defaults = Notification.DEFAULT_LIGHTS;
startForeground(1337, notification);
new Timer().scheduleAtFixedRate(new TimerTask () {
int i=0;
#Override
public void run() {
notificationManagerClass.setContentTitle("Contando");
notificationManagerClass.setContentText(String.valueOf(i));
notificationManagerClass.notificate();
i++;
Log.e("HOLAAA", String.valueOf(i));
}}, 0, 1000);
return START_REDELIVER_INTENT;
}
So.. what I'm missing?
Solved, my error was that the notification id wasnt the same as the startForeground id I was passing (1337)
Edit: It's important to note that the notification id and the service id must not be 0, else it'll blend with the main activity thread