What I have going on is that I made a simple service in Xamarin.Android which for now simply just sends a single Local Push Notification.
In my main application (MainActivity) I have made a statement that checks whether or not this service runs. If it does not, I will simply start the service, otherwise I'll do nothing.
if (UtilityController.IsServiceRunning(typeof(WidgetService), this) == false)
{
StartService(new Intent(this, typeof(WidgetService)));
}
This works fine aswell. No problems here.
Now, the issue is when it comes to my Service being ran multiple times.
[Service]
public class WidgetService : Service
{
public override StartCommandResult OnStartCommand(Android.Content.Intent intent, StartCommandFlags flags, int startId)
{
SendPushNotification();
return StartCommandResult.Sticky;
}
//Other functions such as OnBind, OnDestroy etc..
}
Here I have a service that is straight forward. It's only purpose is to send a Push Notification in the function SendPushNotification(); which it does.
However, when I'm using different StartCommandResult enums, my OnStartCommand function gets triggered in different ways (which I assume is because it restarts the service):
Using StartCommandResult.Sticky makes the Service restart itself every time I close/kill the main app.
Using StartCommandResult.NotSticky makes the Service restart itself every time I start the main app.
This is a problem. I want the Service to only be run ONCE while it's still running. I don't want it ever to be restarted unless I specifically tells it to.
How do I achieve this?
Based on the info you have provided, what is happening should be something like this:
While using StartCommandResult.Sticky: As you have described, service is being restarted when you kill the service process. But this time OnStartCommand is being called with a null intent.
While using StartCommandResult.NotSticky: In this case, service is being stopped when you kill the process but notifications are not removed. When you start the app again, since service is not started, it will start a fresh from your activity.
What you could do depends on the behaviour you do expect from your app. If you want the service to stop when you close/kill the app, you can do something like this:
[Service]
public class WidgetService : Service
{
bool isStarted;
public override StartCommandResult OnStartCommand(Android.Content.Intent
intent, StartCommandFlags flags, int startId)
{
if (!isStarted)
{
SendPushNotification();
isStarted = true;
}
return StartCommandResult.NotSticky;
}
//Only will be called if stopWithTask attribute is set to false
public override void OnTaskRemoved(Intent rootIntent)
{
// Remove the notification from the status bar.
base.OnTaskRemoved(rootIntent);
}
public override void OnDestroy()
{
// We need to shut things down.
// Remove the notification from the status bar.
isStarted = false;
base.OnDestroy();
}
}
And stop the service when you are done with it or app is being closed. But if you want to keep the service running, since you are displaying a notification, I would suggest to make your service a foreground service
Related
I've custom JobIntentService witn static method enqueueWork.
public static void enqueueWork(#NonNull Context context, #NonNull Intent intent) {
enqueueWork(context, MyJobIntentService.class, JOB_ID, intent);
}
Also I've custom implementation of the FirebaseMessagingService. When I receive the push notification from FCM, I call the enqueueWork of my JobIntentService.
MyJobIntentService.enqueueWork(context, new Intent());
But method OnHandleWork not called on Android 8.0 and higher.
My manifest.xml.
<service android:name="com.company.MyJobIntentService"
android:permission="android.permission.BIND_JOB_SERVICE" />
Do you have any ideas why it work not correctly? Thank you.
Nothing incorrectly. Unfortunately, JobIntentService will not run immediately on Android 8.0 and higher.
When running as a pre-O service, the act of enqueueing work will generally start the service immediately, regardless of whether the device is dozing or in other conditions. When running as a Job, it will be subject to standard JobScheduler policies for a Job with a setOverrideDeadline(long) of 0: the job will not run while the device is dozing, it may get delayed more than a service if the device is under strong memory pressure with lots of demand to run jobs.
When testing,I new a empty project, it runs immediately when call, but when i use in a real complex project,it runs several minutes later after called.
Currently, I need a bound (Music)Service, because I need to interact with it. But I also want it to not be stopped, even when all components have unbound themselves.
As the Android Developer Guide says
"[...] Multiple components can bind to the service at once, but when all of them unbind, the service is destroyed."
The Guide also says
"[...] your service can work both ways—it can be started (to run indefinitely) and also allow binding."
In my application, the service is started when the application starts.
I want to have this service destroyed only by a user-click on a close-button I am displaying in a custom notification. But currently, when I am destroying my MainActivity the service also stops.
This is where I am now, this is called when I want to create my Service:
public void createServiceConnection(){
musicConnection = new ServiceConnection(){
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MusicService.MusicBinder binder = (MusicService.MusicBinder)service;
musicSrv = binder.getService();
attachMusicService();
}
};
}
...which calls this:
public void attachMusicService(){
playerFragment.setMusicService(musicSrv);
musicSrv.attach(context); //need this for my listeners, nevermind
bindService(context);
}
...which calls this:
public void bindService(Context act){
if(playIntent==null){
playIntent = new Intent(act, MusicService.class);
act.startService(playIntent);
act.bindService(playIntent, musicConnection, Context.BIND_AUTO_CREATE);
}else{
act.startService(playIntent);
act.bindService(playIntent, musicConnection, Context.BIND_AUTO_CREATE);
}
//finished. I can do stuff with my Service here.
}
Have I misunderstood something?
I feel like the service should keep running, even the activity is destroyed, because I first made a started service and then bound to it.
Bind to your service from custom Application class. I don't think you can keep service alive after activity that's bound to it is destroyed (when onDestroy is called). You can keep service alive if activity pauses (onPause) by calling startForeground from service
Seems like the code was correct.
According to this Question I found out that my problem was the notification I displayed, wich is pretty interesting.
Seems like that a Service that is created for running indefinitely needs to have a Notification wich is displayed by startForeground(NOTIFY_ID, notification);.
I showed my notification with notificationmanager.notify(NOTIFY_ID, notification); before, now I have
`notificationmanager.notify(NOTIFY_ID, notification);
startForeground(NOTIFY_ID, notification);`
and the service won't stop anymore after all my bound Activities are destroyed.
I've been struggling with this question for weeks now. I'm fairly new to Android, hopefully you can give me a hand.
I have this service which runs on a separate thread than the app's. Essentially, the user instructs it to start, and it should stay alive either until the user tells it to stop or until it has served its purpose - it schedules its own destruction (stop) when needed. The service needs to stay alive as it holds important priority-related information, so I can't simply turn to the alarm manager to revive it when needed - though I do use alarm manager for other purposes. I'm having two problems:
First of all, when the user closes the application (by holding the middle button and close the app) the service is destroyed, which means, I lose my data (I'm assuming it gets destroyed as it reboots automatically).
Secondly, it restarts itself, thus causing the data to be re-loaded hence, I lose the data.
As for the activity, its binding to the server through:
private void startService() {
startService(new Intent(this, CES.class));
bindService(new Intent(this, CES.class), mConnection, 0);
}
Finally, the relevant (or at least the ones I find relevant) methods in the Service:
private final IBinder mBinder = new ICESInterface.Stub() { ..... }
#Override
public void onCreate() {
//keeps being called thus I lose my data }
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent,flags,startId);
//return START_STICKY;
}
Let me know if there is more data/information you need.
By returing a binder in onBind you are creating a bound service, which means it is "bound" to your app. This means that it get's destroyed when you app does, that explains the home button destroy. You should be returning null for a background service.
#Override
public void onCreate() {
//keeps being called thus I lose my data
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
You should start your service with START_STICKY flag, so that if the OS destroys your service when running low on resources, it will later recreate it.
Prior to destroying your service, Android will call public void onLowMemory(), there save any data you need so that when it restarts your service, you will be able to do your task again.
EDIT: As per your comment, you would like a service that is running in the background and also allows binding. You can do that if you call the startService() method before any bindService() calls are made... this will effectively make your service a started service instead of bound. In that case you are able to return a IBInder and bind to it.
There is a really, really good article HERE
In my PollFragment.java that able to call new PollTask((MainActivity)getActivity()).execute((Void)null);
And in my PollTask.java
public PollTask(MainActivity activity){
super(activity);
TerminalCfg terminalCfg = Global.getTerminalCfg();
terminalId = terminalCfg.getTerminalId();
retailerAcc = terminalCfg.getRetailerAcc();
internalId = APIUtil.getInternalId(activity);
username = APIUtil.getUsername(activity);
}
And now I want to call the new PollTask((MainActivity)getActivity()).execute((Void)null);
in MyBackgroundService with extends Service like below :
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
new PollTask((MainActivity)getActivity()).execute((Void)null);
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
Is there any other way to replace the getActivity() to call the method?
A Service is a separate component from an Activity and thus you cannot get a reference to it using getActivity(). Services are designed for doing work not visible to the user, including (but not limited to) background work on a separate thread from the UI thread. Services are more robust and offer more control over what work is being performed that is not visible to the user. They do not require an Activity to run.
An AsyncTask is a simple way of doing work from inside an Activity on a separate Thread from the UI thread.
Basically, you dont want or need an AsyncTask in a Service.
Instead, in your Service you should either spawn a Thread, or use IntentService which will handle creating a worker Thread for you. Then when you are finished, send an intent back to the Activity either by starting it or using a LocalBroadcast
Alternatively, you can tie a Service to an Activity and provide methods that the Service and Activity can call directly on each other through an IBinder interface. These are called bound services and will only be alive as long as the Activity is alive.
Try an IntentService
I think your best bet is to try learning how to use an IntentService
http://developer.android.com/reference/android/app/IntentService.html
I'm working on an Android app that needs to maintain a network connection to a chat server. I understand that I can create a service to initiate the connection to the server, but how would the service notify an Android Activity of new incoming messages? The Activity would need to update the view to show the new messages. I'm pretty new to Android, so any help is appreciated. Thanks!
Can you pass a handler to your service?
First, define your handler as an interface. This is an example, so yours may be more complex.
public interface ServerResponseHandler {
public void success(Message[] msgs); // msgs may be null if no new messages
public void error();
}
Define an instance of your handler in your activity. Since it's an interface you'll provide the implementation here in the activity, so you can reference the enclosing activity's fields and methods from within the handler.
public class YourActivity extends Activity {
// ... class implementation here ...
updateUI() {
// TODO: UI update work here
}
ServerResponseHandler callback = new ServerResponseHandler() {
#Override
public void success(Message[] msgs) {
// TODO: update UI with messages from msgs[]
YourActivity.this.updateUI();
}
#Override
public void error() {
// TODO: show error dialog here? (or handle error differently)
}
}
void onCheckForMessages() {
networkService.checkForMessages(callback);
}
and NetworkService would contain something like:
void checkForMessages(ServerResponseHandler callback) {
// TODO: contact server, check for new messages here
// call back to UI
if (successful) {
callback.success(msgs);
} else {
callback.error();
}
}
Also, as Aleadam says, you should also be away that a service runs on the same thread by default. This is often not preferred behavior for something like networking. The Android Fundamentals Page on Services explicitly warns against networking without separate threads:
Caution: A service runs in the main thread of its hosting process—the service does not
create its own thread and does not run in a separate process (unless you specify
otherwise). This means that, if your service is going to do any CPU intensive work or
blocking operations (such as MP3 playback or networking), you should create a new thread
within the service to do that work. By using a separate thread, you will reduce the
risk of Application Not Responding (ANR) errors and the application's main thread can remain dedicated to user interaction with your activities.
For more information on using threads in your service, check out the SO articles Application threads vs Service threads and How to start service in new thread in android
Did you check the Service API page: http://developer.android.com/reference/android/app/Service.html ?
It has a couple of examples on how to interact with a Service.
The service runs on the same thread and the same Context as the Activity. Check also here: http://developer.android.com/reference/android/content/Context.html#bindService%28android.content.Intent,%20android.content.ServiceConnection,%20int%29
Finally, take a look also at Lars Vogel's article: http://www.vogella.de/articles/AndroidServices/article.html
One common and useful approach is to register a broadcast receiver in your Activity, and have the Service send out notification events when it has useful data. I find this to be easier to manage than implementing a handler via a callback, mainly because it makes it easier and safer when there is a configuration change. If you pass a direct Activity-reference to the Service then you have to be very careful to clear it when the Activity is destroyed (during rotation, or backgrounding), otherwise you get a leak.
With a Broadcast Receiver you still have to unregister when the Activity is being destroyed, however the Service never has a direct reference to the Activity so if you forget the Activity will not be leaked. It is also easier to have the Activity register to listen to a topic when it is created, since it never has to obtain a direct reference to the Service...
Lars Vogel's article discusses this approach, it is definitely worth reading! http://www.vogella.com/tutorials/AndroidServices/article.html#using-receiver