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
}
})
Related
I am confused right now , about service concept of running and stopping:
what i want to do:
Start Location service at the very start of application.
Keep getting location updates and store them to shared preference
Stop the service in onDestroy of Application scope!
So far i have searched and studied we can only do following things with service(correct me if i'm wrong):
Stop the service automatically by binding it to related activities/fragments/views , when all of them destroyed service unbind itself automatically so we can call stopself method in unbind
return START_NOT_STICKY in onStartCommand to tell OS , don't recreate it , and create intent local service , after completion of some work it will destroy itself.
Stopping the service manually , by declaring it's intent in some kind of static scope and stopping the service in on onActivityDestroyed of Application class [I am not sure what will happen? , maybe service will destroy each time any activity will be destroyed ? or it will be destroyed only when overall application get's destroyed?]
Either way , i am bit confused and beat , been trying to adjust my Location service with given details for 2 days
If you start your Service using START_NOT_STICKY, then your app will kill your service once your entire application is closed from background i.e. you cleaned your app from home screen.
Here START_NOT_STICKY states that you need not recreate service in case it is been killed.
If this is not the case then you have to manually kill it by your self.
Like
Intent lintent = new Intent(context, LocationService.class);
context.stopService(lintent);
You can use this code at point where your application kills.
That's it. You are good to go with this.
First of all, launch the "LocationService" on your app start:
public class MyApp extends Application {
private static final String TAG = "MyApp";
#Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "App started up");
startService(new Intent(this, MyLocationService.class));
}
}
Second :
As you said, the Service should better run with the "START_NOT_STICKY" flag
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_NOT_STICKY;
}
Thrid:
Once the system kills your app, the Service will automatically be killed, so no problems at all.
There is no onDestroy() method on the Application object, the only similar event is onTerminated() and it is not being launched on production devices.
onTerminate
Added in API level 1 void onTerminate () This method is for use in
emulated process environments. It will never be called on a production
Android device, where processes are removed by simply killing them; no
user code (including this callback) is executed when doing so.
More information:
https://developer.android.com/reference/android/app/Application.html#onTerminate()
Btw, If you want the MyLocationService to send updates of the location to your app (when it is open), you should consider to use Otto or EventBus (I recommend you this last one because of the simplicity to use it). You can even configure the #Suscriber to receive updates of old retrieved locations if you want.
I will try to explain in a easiest way :)
There are two type of service
IntentService and Service
IntentService when started will kill itself once it treated the content of it onHandleIntent method
as for Service well this one will not end until you give it the command to do so even if your start it using the context of an activity. It will also stop when the application is stopped in an extreme case (by system (Settings/application/YourApp/stop app OR an app crash)
Easiest way is First of all start IntentService with AlarmManager at some repeating time (10 mintutes), then in onHandleIntent() get Location and store into preference.
No Need bind to your activity, the IntentService automatically stops itself after saved in preferences.
Yes you can stop the service in onDestroy() of the activity:
#Override
public void onDestroy(){
Log.v("SERVICE","Service killed");
service.stop();
super.onDestroy();
}
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));
}
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;
}
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.