When I start a service (IntentService) it will call the onHandleIntent() which is the service function. While it is running, if I call startService() again, it caches the invoke, and calls the onHandleIntent() again. Actually what I want to do is, pass some new data to the current running service instance, without creating an additional. (Like signaling in threads or a listener).
How do I do it?
In my onHandleIntent() I want to use while(true){....} , so it will run forever.
Extend the normal Service class. On the first onStartCommand() that you receive, start a background thread that has your "while(true) {...}" code. On the onStartCommand() calls thereafter, check for the existence of your worker thread and when it exists, signal it.
Without knowing what you are going to use this Service for it is hard to give you any better advice. However, some things to think about:
- Check out the Looper/Handler classes that are also used by the IntentService.. They may give you some nice way to code your "while(true)" loop, including signalling.
- Remember that the onStartCommand() method should return within a few seconds (it is running on the main thread) so if you want to hand off new work (that is received through the Intent passed in onStartCommand) you should make sure this hand-off doesn't take too long. The Looper/Handler classes may help you here as well.
Check whether your service is already started, if not start it:
private boolean isMyServiceRunning() {
ActivityManager manager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if ("com.example.Service".equals(service.service.getClassName())) {
return true;
}
}
return false;
}
Related
I have a little specific issue and I want some tips for that. I need to start just one time an Android service, and I can use these 2 options:
Start the service from the onCreate() method of the MainActivity. The problem with this way is when the device is rotated with my app running, the onCreate() method is called again, because the MainActivity is restarted and I don't want that my service restarted because of this.
Start it from the Android Application class. The problem is that before start the service, I need to check if some permissions was accepted by the user. So the first time that my application is started in the Application class the permissions are not accepted yet.
Reading this article Handle configuration changes I can see that is possible avoid the restart of my MainActivity but I don't know if this is a good practice. So maybe exist some way to request user permissions from the Application
onCreate with be called once during Service lifecycle, but onStartCommand will be called every time you call startService
You can't request user permission outside Activity context. So, you can run service but you are unable to do something that requires permission. You may register BroadcastReceiver inside your service for catching some "permission granted" intents. But this intent should be created and sended to service via broadcast in Activity context at the moment when user grants permission. Or you may use service commands instead
To prevent restarting your service just check in your MainActivity onCreate method is your service running
Function for checking service running:
private boolean isMyServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
And after that you just need to call it in your onCreate method:
#Override
public void onCreate(Bundle savedInstanceState) {
if(!isMyServiceRunning(MyService.class)) {
//start your service
}
}
I am not sure the reason why you would like to start the service only once but I am gonna try to suggest an alternative to achieve this.
Since you would like to ask for a user's permissions, the service cannot be started in the application class. So the only place you can start it is in the activity class.
The key to solve this will be the onCreate and onStartCommand methods.
If you define the service in the manifest file and then call Context.startService(context) multiple times, onCreate will be called only once as long as the system has not killed the service yet.
onStartCommand will be called every time you call Context.startService(context).
Hence in this approach, write the block of code that you need to be executed once in the onCreate method. For all updates, execute them in the onStartCommand method.
Another option would be to dig deeper into android architecture components (LiveData, LifeCycle and observers)
I think the best way to deal with android's infamous rotation is to use ViewModel. As ViewModel survives device's rotation you can store a liveData of boolean initially set to true and observe it in onCreate(). Start the service only if the observed value is true and set it to false once you have started the service. Please see the following example,
In ViewModel
val startService = MutableLiveData<Boolean>(true)
In Activity's onCreate()
viewModel.startService.observe(this, Observer {
if (it) {
startService()
viewModel.startService.value = false
}
})
I have a Foreground Service for my app, which must run the whole time and never stops. While I was testing it few times, it seems after 3-4 days Android decides it just to stop it, and never restart it again, and in my Activity I have a check for the service if its running, and if it isnt to start it.
Even though, the service is not starting, and I can't debug it, because this is not happening immidiatelly.
So this is my check, is there something wrong in it?
if (!isMyServiceRunning(MyService.class)) {
startService(new Intent(this, MyService.class));
}
private boolean isMyServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
I call this check in onResume() of the Activity, and I can't just run it everytime when the activity starts, because I have a constantly running Thread in the service, which will get more than one instance if started several times...
So here is what I want:
Either have more reliable way to check for the service if its running
or
Just start the service every time the activity starts, to prevent problems, and the thread inside of it must always have just 1 instance (I have already declared is as static)
You can make your service Bindable and bind your Activity to the service when the Activity starts/resumes. This way you will be sure the service is running. But I think you have a problem with your service. My I ask you to post more information about the service? It is not normal the OS to kill a foreground service with no traces.
I have an application which is doing some repetitive background tasks. I'm using service and Alarmmanager with setrepeating method. The only way to stop this service is a button which appears on my activity screen (of course android system can kill it for any reason at any time but I don't care what android system does). My problem is when my activity is destroyed by android system after a while, I have a service which is running forever unless Android kills it. My question is how can I get a reference of this service to stop it after creating new activity(I assume I'm creating new activity after the old one destroyed)?.
Consider bindService and unbindService.
When your activity is created, you can call bindService, and use the returned binder in order to communicate with your service. A service can be both bound and started, in case you don't want unbind to kill your service after your activity ends.
Another approach could be using a LocalBroadcastManager in order to communicate between your activity and service.
Finally, consider either Otto or Eventbus - two awesome opensource projects for communicating between components in your android project.
Use the bind service and unbind service methods on your MainActivity so that you can call unbind service onDestroy() when ever your apps activity is either explicitly destroyed by user or the android system, the service class is destroyed alongside.
Use this post as a guide: Example: Communication between Activity and Service using Messaging
I think I couldn't explain my problem clearly. But I found my answer at this post How to get reference to running service?
Dawid Sajdak answer
private boolean isMyServiceRunning() {
ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if ("your.service.classname".equals(service.service.getClassName())) {
return false;
}
}
return true;
}
if(isMyServiceRunning()) {
stopService(new Intent(ServiceTest.this,MailService.class));
}
I've created an Started Service from an activity. I'm curious about: what happens if the activity gets destroyed? Does the service still run? (I think it runs!)
Then when the activity is re-created, is there any way to communicate with that service? I just need to know whether the service is still running, if running, then I want to pass a reference of the activity to the service.
Okay, a comment isn't big enough. Here's some code I use to start a service. Following the start service is a function I use to determine if the service is running. The class DownloadDatabase extends IntentService. The service downloads a 70 megabyte SQLite database that was created externally to Android.
Intent intentService = new Intent(this, DownloadDatabase.class);
startService(intentService);
The following function checks to see if the service is already running. It is coded as a function because I call it from other routines that do other verifications if is not running.
private boolean isMyServiceRunning() {
ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager
.getRunningServices(Integer.MAX_VALUE)) {
if ("com.hodsonssoftwarellc.nutritionexplorer.DownloadDatabase"
.equals(service.service.getClassName())) {
return true;
}
}
return false;
}
You will also need the following permission in your manifest:
uses-permission android:name="android.permission.GET_TASKS"
What is happening in the isMyServiceRunning function is as follows:
The Activity Manager is acquired.
A list of tasks that are currently running is retrieved and used as the argument in the special case for loop.
As each task is retrieved from the list, the task name is compared to my fully qualified class name for the download service.
If a match is found, the function returns true. Otherwise it returns false.
So, in answer to your question, yes, you can check to see if it running from your task that starts it. Even if you exit the task and start it again. You can also check if is running from a different activity or app.
Note: This code was written for 2.x compatibility. If you are doing a download in version 3 or higher, use the download manager instead of writing your own download service.
I have read the other threads about checking if a service is running but this is not working for me.
My situation is that am creating a background web server service but not binding to it because I want it to continue running after the activity ends. The service creates a notification so users can see it is running. The user can stop the service through a button on the Activity.
This is all working fine, except on launch of the activity, I can't determine if the service is already running. Am using the following:
if (isMyServiceRunning() == false)
{
Intent ws = new Intent(this, WebServerService.class);
startService(ws);
}
And this
private boolean isMyServiceRunning() {
String sClassName;
ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE))
{
sClassName = service.service.getClassName();
DebugMsg("Service: " + sClassName);
if (sClassName.contains("com.<mydomain>.webservice"))
{
return true;
}
}
return false;
}
I get a list of services running, for both system and 3rd party services. But my service doesn't show up in the list, even though I know it's running. If I go into the phone's Settings -> Applications -> Running Services, I can see it running there.
I read in the documentation somewhere that calling startService on a service that is already running should be ignored. But that isn't the case as I can see in the debugger that both OnCreate and OnStart are being called.
It is important that I do not create a new service each time because the background service may be in the middle of serving a file. The activity does not need to do any communication with the service - only start it if it isn't running and kill it if the user hits a button.
Any idea on why my service is not showing up in the getRunningServices list?
Step #1: Add static boolean isRunning=false to your service.
Step #2: Set isRunning to true in onCreate() of the service.
Step #3: Set isRunning to false in onDestroy() of the service.
Step #4: Examine isRunning to see if the service is running.
I read in the documentation somewhere that calling startService on a service that is already running should be ignored. But that isn't the case as I can see in the debugger that both OnCreate and OnStart are being called.
I am very confident that onCreate() is not called when startService() is invoked on a running service. onStartCommand() (and, hence, onStart() for older services) will be called for every startService() call.
It is important that I do not create a new service each time
Services are natural singletons. There will be precisely 0 or 1 copies of the service in memory. There will never be 2 or more.
Actually, my service is now showing up in the list of services. I'm now thinking that maybe the service name wasn't registered until after restarting the phone, because I didn't make any changes but everything is working now after restarting.