I'm writing two applications that cooperate together one collecting data (using a service) and the other is a custom keyboard getting the data from the service and writing them to whatever text field is selected. All this is working fine my only problem is the service binding; right now the service will be started from any app when they call bind. That is not the behavior i want to have; The data collection app should be the only one starting / stopping the service via an activity, the keyboard will only bind if the service is running. what i need to do that is check if the service is running before calling the bind. is there anyway for me to check the status from the keyboard app ? is there some kind of lock to signal the presence of the service ? all i find right now is methods that require the class name and context from where the service was started and that doesn't work for me.
basically ended with three possibilities hope this helps someone else in the future.
Method 1: (what i went with event though method 2 is better)
public static boolean isServiceRunning(Context queryingContext) {
try {
ActivityManager manager = (ActivityManager) queryingContext.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if ("com.example.myservice".equals(service.service.getClassName()))
return true;
}
} catch (Exception ex) {
Toast.makeText(queryingContext, "Error checking service status", Toast.LENGTH_SHORT).show();
}
return false;
}
Method 2:
like pskink suggested above use bindService with flag 0. only issue with this is the method will always return true whether it got connected or not, you'll have to find another way to know what happened (you won't receive the service handle if it failed)
Method 3:
suggested by yosriz use SharedPreference to stop a status flag that the other application will check and know the status of the service.
Service cannot be checked to see if it was started already, Android Services are designed to handle multiple client, and when bind method will create the service if not running already.
So you can either start it explicitly or making Bound Service connection with bind() that will create new if not exist.
Nevertheless, you can achieve this functionality in many ways, for instance save the service 'is running' state in some shared preference and check this pref before binding to the service. (SharedPreference can be shared between 2 process of the same user id)
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
}
})
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;
}
There are lots of reasons why detecting if application is on foreground.
for example - as a trigger to GCM/C2DM push notification - lot's of apps would have good reason implementing different behavior when app is foreground and background.
Other reason could be - shotting down services consuming precious resources, such as querying servers in background task for instance.
Just to be clear: the definition (as I see it) for background app is:
application which none of it activities are invoked the onStart() method, and did not invoke yet the onStop() method.
that's because activity is visible to the user in it life cycle only at that time.
From the other hand -
seems like Google don't want application to react to the home button (it's not part of the API)
reacting to the onBackPressed() on the "root / main" activity as indicator for leaving Activity certainly not good idea (because lots of users using the home button, and not the back button)
there is no method in the API allowing determine if app is foreground (according to my definition..)
if I didn't miss something in the API, and it's really the case - Why there is no why to determine easily if the application is foreground or not????!!!!
what I know I can do to determine if the application is foreground is described in this thread - How to detect when an Android app goes to the background and come back to the foreground
but as #Emil saying - it's requiring special permission, or requiring some tricky logic's which very fast becoming problematic to maintain, and it smells like bad approach (although that's what I'm doing for now, because I don't have better idea...)
my questions basically are:
Is there no such API method from good reason?
Is taking into account if application is foreground or not is a bad approach?
Is there any other way to know if application is foreground or not?
is taking into account if application is foreground or not is a bad approach?
Taking foreground versus background into account is reasonable.
is there any other way to know if application is foreground or not?
You can roughly divide the scenarios for this into two groups:
Cases where you want to take an action immediately upon a change in the foreground/background status
Cases where some other event occurs (AlarmManager alarm, incoming system broadcast, etc.), and at that point you want to take different actions based upon whether or not you are in the foreground
In the former case, onUserLeaveHint() is your most reliable simple option. I cannot guarantee that it will cover all cases, but it should handle the HOME scenario, for example. You are also welcome to maintain a reference count of started activities in a static data member and try to use it instead.
In the latter case, an ordered broadcast can be useful.
With the new Android Architecture Components there is an easy way to know if your app is in the foreground or the background.
Just like with the activity scope lifecycle owner there is a general process lifecycle owner which you can subscribe to and get lifecycle updates.
For example:
Add this code in order to register as a lifecycle observer
ProcessLifecycleOwner.get().lifecycle.addObserver(lifecycleListener)
And this code in order to receive the relevant callbacks
#OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onApplicationOnStartEvent() {
}
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void onApplicationOnStopEvent() {
}
Don't forget to remove the observer once you don't need it
ProcessLifecycleOwner.get().getLifecycle().removeObserver(lifecycleListener);
More information and examples can be found in this excellent article:
https://proandroiddev.com/detecting-when-an-android-app-backgrounds-in-2018-4b5a94977d5c
I had the same issue. I want to display push notification when my activity is not in foreground mode. Please go through following code and you will get your answer.
Context ctx = context.getApplicationContext();
ActivityManager am = (ActivityManager) context
.getSystemService(ACTIVITY_SERVICE);
// get the info from the currently running task
List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
PackageManager pm = this.getPackageManager();
try {
/**
* take fore ground activity name
*/
ComponentName componentInfo = taskInfo.get(0).topActivity;
if (printLog == true) {
Log.d("Home", "CURRENT Activity ::"
+ taskInfo.get(0).topActivity.getClassName());
Log.d("Home", "Number Of Activities : "
+ taskInfo.get(0).numRunning);
Log.d("Home",
"Componenet Info : " + componentInfo.getPackageName());
Log.d("Home",
"Componenet Info : " + componentInfo.getClassName());
}
/**
* All activities name of a package to compare with fore ground
* activity. if match found, no notification displayed.
*/
PackageInfo info = pm.getPackageInfo(
"<PackageName>",
PackageManager.GET_ACTIVITIES);
ActivityInfo[] list = info.activities;
for (int i = 0; i < list.length; i++) {
Log.d("TAG","Activity : "+list[i].name);
}
} catch (NameNotFoundException e) {
e.printStackTrace();
}
To use this, you have to take permission in your manifest file.
uses-permission android:name="android.permission.GET_TASKS"
Pardon me if I can't get your question.
If it is necessery for you to know if the app is on backround or foreground from a service that is running on the background(doesnt make sense otherwise), then you can just use binding, that is - bind to it on all your activities onResume, and unbind on all activities onPause. then in your service you can manage not only your application visibility to the user, but also which of the activities are open at any time. it is also leak-proof and more stable then a static variable (which can be cleaned up if necessery) as you are using android's API and relying on the correctness of the android OS code itself.
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.