I have an app that creates a never ending background service. The service is started when the app is launched. When the app is killed (e.g. by the user), the service sends a broadcast request that will restart it after it is killed.
The question is: when I restart the app, how can I know if the service is already running?
Naively I had thought that by restarting the service when the app is re-launched it would have stopped the existing service but this does not happen. The following code shows that if I do this, there are two services running at the same time (the printout in the timer moves from every second to every half a second).
Many thanks
public class MainActivity extends AppCompatActivity {
static Intent mServiceIntent;
private SensorService mSensorService;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ctx=this;
setContentView(R.layout.activity_main);
mSensorService= new SensorService(getCtx());
mServiceIntent = new Intent(getCtx(), mSensorService.getClass());
startService(mServiceIntent);
}
Context ctx;
public Context getCtx() {
return ctx;
}
#Override
protected void onDestroy() {
stopService(mServiceIntent);
Log.i("MAINACT", "onDestroy!");
super.onDestroy();
}
}
public class SensorService extends Service {
public int counter=0;
public SensorService(Context applicationContext) {
super();
Log.i("HERE", "here I am!");
}
public SensorService() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
startTimer();
return START_STICKY;
}
private Timer timer;
private TimerTask timerTask;
long oldTime=0;
public void startTimer() {
//set a new Timer
timer = new Timer();
//initialize the TimerTask's job
initializeTimerTask();
//schedule the timer, to check if the service is there in 25s
timer.schedule(timerTask, 1000, 1000); //
}
/**
* it sets the timer to check in x seconds if the sensorsService is active. If not it will start the service
*/
public void initializeTimerTask() {
timerTask = new TimerTask() {
public void run() {
long time= System.currentTimeMillis();
Log.i("in timer", "in timer ++ "+(time-oldTime)+" ++ "+ (counter++));
oldTime= time;
}
};
}
/**
* not needed
*/
public void stoptimertask() {
//stop the timer, if it's not already null
if (timer != null) {
timer.cancel();
timer = null;
}
}
#Override
public void onDestroy() {
super.onDestroy();
Log.i("EXIT", "ondestroy!");
Intent broadcastIntent = new Intent("uk.ac.shef.oak.ActivityRecognition.RestartSensor");
sendBroadcast(broadcastIntent);
stoptimertask();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
public class SensorRestarterBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.i(SensorRestarterBroadcastReceiver.class.getSimpleName(), "Service Stops! Oooooooooooooppppssssss!!!!");
context.startService(new Intent(context, SensorService.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;
}
And then use
isMyServiceRunning(MyService.class)
Credit
Related
Here I attached my code,I created a service which runs both cases while my app is in background and killed state. It is not running on android marshmallow devices, but it works on other devices. When I check other devices, Log will be executed when the app is kill state, only version 6 , not executed.
public class SensorService extends Service {
public int counter=0;
public SensorService(Context applicationContext) {
super();
Log.e("HERE", "here I am!");
}
public SensorService() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
startTimer();
return START_STICKY;
}
When comes to destroy again I will call the service, It will go to broadcast receive, and then start service function will call.
#Override
public void onDestroy() {
super.onDestroy();
Log.e("EXIT", "ondestroy!");
Intent broadcastIntent = new Intent("sales.com.ActivityRecognition.RestartSensor");
sendBroadcast(broadcastIntent);
stoptimertask();
}
private Timer timer;
private TimerTask timerTask;
long oldTime=0;
public void startTimer() {
timer = new Timer();
initializeTimerTask();
timer.schedule(timerTask, 1000, 1000); //
}
public void initializeTimerTask() {
timerTask = new TimerTask() {
public void run() {
Log.e("in timer", "in timer ++++ "+ (counter++));
}
};
}
public void stoptimertask() {
//stop the timer, if it's not already null
if (timer != null) {
timer.cancel();
timer = null;
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
Below code for Broadcast Receiver code : app works, when i click back button, if the app was killed state, it is not working, other devices services running.
public class SensorRestarterBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.e(SensorRestarterBroadcastReceiver.class.getSimpleName(), "Service Stops! Oooooooooooooppppssssss!!!!");
// context.startService(new Intent(context, SensorService.class));
Intent i = new Intent(context, SensorService.class);
context.startService(i);
}
}
Run a service in background continuously. For example, a service has to be kicked off which will display a toast message 20 seconds once even if the app is closed.
public class AppService extends IntentService {
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
public AppService() {
super("AppService");
}
#Override
protected void onHandleIntent(Intent workIntent) {
Toast.makeText(getApplicationContext(), "hai", Toast.LENGTH_SHORT).show();
SystemClock.sleep(20000);
}
}
Below code works for me...
public class AppService extends Service {
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate() {
Toast.makeText(this, " MyService Created ", Toast.LENGTH_LONG).show();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, " MyService Started", Toast.LENGTH_LONG).show();
return START_STICKY;
}
}
Accepted answer will not work on from Android 8.0 (API level 26), see the android's background limitations here
Modification in Accepted Answer:
1: You have to invoke the service's startForeground() method within 5 seconds after starting the service. To do this, you can call startForeground() in onCreate() method of service.
public class AppService extends Service {
....
#Override
public void onCreate() {
startForeground(9999, Notification())
}
....
}
2: You must call startForegroundService() instead of startService() by checking API level from where you want to start the service.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
} else {
context.startService(intent);
}
This code work for me..
public class ServiceClass extends Service {
public static final int notify = 300000; //interval between two services(Here Service run every 5 Minute)
private Handler mHandler = new Handler(); //run on another Thread to avoid crash
private Timer mTimer = null; //timer handling
#Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public void onCreate() {
if (mTimer != null) // Cancel if already existed
mTimer.cancel();
else
mTimer = new Timer(); //recreate new
mTimer.scheduleAtFixedRate(new TimeDisplay(), 0, notify); //Schedule task
}
#Override
public void onDestroy() {
super.onDestroy();
mTimer.cancel(); //For Cancel Timer
Log.d("service is ","Destroyed");
}
//class TimeDisplay for handling task
class TimeDisplay extends TimerTask {
#Override
public void run() {
// run on another thread
mHandler.post(new Runnable() {
#Override
public void run() {
Log.d("service is ","running");
}
});
}
}
}
In your manifest, where you declare your service, add:
android:process=":processname"
This lets the service run on a separate process and thus it will not be killed with the app.
You can then chose if you want to use foreground. It will show a persistent notification, but reduces the likelihood if the service being killed.
Further, if you want to create a continuously running service, use Service, NOT IntentService. IntentService stops when it is finished doing its action.
I have a Bound service which collects locations for 3 minutes every 15 minutes. I start a CountDownTimer once the Service is connected in onServiceConnected of ServiceConnection.
I receive all the Timer callbacks (onFinish) (onTick) perfectly as far the Activity which bindService is visible.
When Device is locked I do not receive any updates from the timer.
MyLocationService
public class MyLocationService extends Service implements MyTimerListener{
private IBinder mBinder = new MyLocationBinder();
public void onCreate() {
locationManager = new MyLocationManager(this);
}
#Override
public IBinder onBind(Intent arg0) {
return mBinder;
}
public class MyLocationBinder extends Binder
{
public MyLocationService getService()
{
return MyLocationService.this;
}
}
public void startFetchingLocations()
{
if(locationManager != null){
locationManager.startFetchingLocations();
if(publishPeriodCountDownTimer != null) {
publishPeriodCountDownTimer.cancel();
publishPeriodCountDownTimer = null;
}
publishPeriodCountDownTimer = new MyCountTimer(gpsPublishPeriod * 60 * 1000, 1000, MyLocationService.this);
publishPeriodCountDownTimer.start();
}
}
#Override
public void onTimeFinished() {
//Start fetching locations
locationManager.startFetchingLocations(); //Start 3 min CountdownTimer
}
#Override
public void onTick() {
}
public void onAllLocationsRecieved(ArrayList<Location> locations)
{
//Do stuff on Locations
locationManager.stopFetchingGPS(); //Stops 3 min countdownTimer
}
}
MyActivty
public class MyActivity
{
#Override
protected void onStart() {
super.onStart();
btnStop.setVisibility(View.VISIBLE);
MyNoificationManager.cancelAllNotifications();
if (!isLocationServiceBound) {
Intent locServiceIntent = new Intent(this, MyLocationService.class);
bindService(locServiceIntent, locationServiceConnection, Context.BIND_AUTO_CREATE);
}
}
private ServiceConnection locationServiceConnection = new ServiceConnection(){
#Override
public void onServiceDisconnected(ComponentName name) {
isLocationServiceBound = false;
}
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MyLocationBinder locationBinder = (MyLocationBinder) service;
locationService = locationBinder.getService();
isLocationServiceBound = true;
locationService.startFetchingLocations();
}
};
}
It works as expected when the Activity is visible. The timer doesn't provide any onTick() or onTimeFinished() callbacks when the device is locked.
What could be the problem here ?
The CountDownTimer will not work when the phone goes to sleep mode. This is by design and it's to save battery life.
Alternately you can use android.app.AlarmManager instead to achieve your goal. Following is the sample code how to do that. Set the alaram like below
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, MyLocationService.class);
intent.putExtra("need_to_fetch_loc", true);
PendingIntent alarmIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
alarmManager.set(AlarmManager.RTC_WAKEUP, gpsPublishPeriod * 60 * 1000, alarmIntent);
Add the following method to your MyLocationService class
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if(locationManager!= null && intent.getBooleanExtra("need_to_fetch_loc", false))
{
locationManager.startFetchingLocations();
}
return super.onStartCommand(intent, flags, startId);
}
I need to implement such a procedure:
Start a background service
Update the service with parameters (from UI - user input)
After activity ended the service should keep on running and preform requests to HTTP server every minute. in this stage i still need the parameters I updated in the second stage - I send them to the server.
The service should store the server last response and compere each with the last. if there is a change, notify the user.
Finally, when the activity starts again, the service should update UI with latest the server response.
What I tried:
BroadcastReciver - The problem is after onRecive ended all the arguments which aren't declared as final will wipe out, as well as I didn't found a way to update the Intent being sent automatically every minute.
Service - Using startService() - The problem is when the activity ended the service like stops and starts , flushing all it's arguments. and once again I didn't figured out how to update the arguments after the service is already started.
So how to handle such a situation?
Thanks.
It sounds like what you need to do is to be able to "bind" to your service. What I have posted below is a simple template of how to do that. For your purposes you will need to store variables in your Service class and create getters so that when you re-launch your activity you can get the most up to date variables. Also - please note that I start and stop the Service example below in onResume and onPause. You will no doubt want to do this differently.
//Activity
//Bind to Service Example
public class ExampleActivity extends Activity implements OnClickListener {
// UI
private Button binderButton;
// service
private MyService myService;
private Intent serviceIntent;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_layout);
// binder button
binderButton = (Button) findViewById(R.id.button1);
binderButton.setOnClickListener(this);
binderButton.setText("start");
serviceIntent = new Intent(this, MyService.class);
}
private ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
myService = ((MyService.MyBinder) service).getService();
}
#Override
public void onServiceDisconnected(ComponentName name) {
myService = null;
}
};
#Override
protected void onResume() {
super.onResume();
// start the service
startService(serviceIntent);
// bind to the service
bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button1:
// call method within the service
myService.doServiceStuff();
break;
}
}
#Override
protected void onPause() {
super.onPause();
stopService(serviceIntent);
unbindService(serviceConnection);
}
}
//Service
public class MyService extends Service {
private final IBinder binder = new MyBinder();
#Override
public IBinder onBind(Intent arg0) {
return binder;
}
public void doServiceStuff() {
task.execute();
}
// create an inner Binder class
public class MyBinder extends Binder {
public MyService getService() {
return MyService.this;
}
}
AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
Log.d("yourTag", "long running service task");
return null;
}
};
}
Thanks javaJoe, although your answer didn't solved my problem it gave me some a good ideas.
What I did:
in the Activity onCreate, check if my service is running, if so bind it else, create new one and bind it.
Transferring arguments between the Service and the Activity using setters and getters.
in the Activity onDestroy (the problem was that the service calls self Destory) the Activity sends the final arguments through Intent to a Broadcastreciver. The Broadcastreciver than starts the Service again, initiating it with the correct arguments.
I don't know if this architecture is ideal, i'd like to get some feedback.
Here is the code:
Activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Set Service Intent
serviceIntent = new Intent(this, UpdateService.class);
if (isMyServiceRunning()) {
//Bind to the service
bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}else{
updateService=new UpdateService();
//Start the service
startService(serviceIntent);
//Bind to the service
bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}
}
private boolean isMyServiceRunning() {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (UpdateService.class.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
private ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
updateService = ((UpdateService.MyBinder) service).getService();
//Set Initial Args
updateService.setParams(int arg0);
}
#Override
public void onServiceDisconnected(ComponentName name) {
updateService = null;
}
};
#Override
protected void onDestroy() {
//UnBind from service
unbindService(serviceConnection);
//Stop Service
stopService(serviceIntent);
//Prepare intent to broadcast reciver
Intent intent = new Intent(MainActivity.this,ServiceRunnerBCR.class);
intent.setAction(ServiceRunnerBCR.ACTION_SET_UpdateService);
intent.putExtra(ServiceRunnerBCR.keyVal_arg0, arg0);
intent.putExtra(ServiceRunnerBCR.keyVal_arg1, arg1);
//Send broadcast to start UpdateService after the activity ended
sendBroadcast(intent);
super.onStop();
}
Broadcastreciver:
public class ServiceRunnerBCR extends BroadcastReceiver {
public static final String ACTION_SET_UpdateService = "ACTION_ALARM";
public static final String keyVal_arg0="ARG0";
public static final String keyVal_arg1="ARG1";
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ACTION_SET_UpdateService)){
updateIntent(context, intent.getDoubleExtra(keyVal_arg0, 0.02), intent.getStringExtra(keyVal_arg1));
}
}
private void updateIntent(Context context, double arg0, String arg1){
Intent intent = new Intent(context,UpdateService.class);
intent.setAction(ACTION_SET_UpdateService);
intent.putExtra(keyVal_arg0, arg0);
intent.putExtra(keyVal_arg1, arg1);
synchronized (this){
try {
this.wait(6000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
context.startService(intent);
Log.d("OREN","ServiceRunner");
}
}
Service:
public class UpdateService extends Service {
private final IBinder binder = new MyBinder();
public static final String keyVal_arg0="ARG0";
public static final String keyVal_arg1="ARG1";
private Timer timer;
private HTTPHandler http = new HTTPHandler();
private int test=0;
double arg0=0;
String arg1= "";
private TimerTask updateTask = new TimerTask() {
#Override
public void run() {
test++;
Log.d("OREN", "Timer task doing work " + test + " arg0: " + arg0);
//Do some work here
}
};
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent!=null){
arg0=intent.getDoubleExtra(keyVal_arg0, 0.002);
arg1=intent.getStringExtra(keyVal_arg1);
timer = new Timer("UpdateTimer");
timer.schedule(updateTask, 1000L, 10 * 1000L);
Log.d("OREN", "ServiceStarted" + test);
}
return super.onStartCommand(intent, flags, startId);
}
#Override
public IBinder onBind(Intent intent) {
Log.d("OREN", "OnBind" + test);
return binder;
}
public void setArg0(double d){
arg0=d;
}
// create an inner Binder class
public class MyBinder extends Binder {
public UpdateService getService() {
return UpdateService.this;
}
}
#Override
public void onDestroy() {
Log.d("OREN", "OnDestroy" + test);
super.onDestroy();
}
#Override
public boolean onUnbind(Intent intent) {
Log.d("OREN", "OnUnBind" + test);
return super.onUnbind(intent);
}
}
I have a service that performs an action on the timer. Service runs from activity. While the application is running, the service works fine.If I close the application using the cleanup in the launcher, the service goes restart mode, but does not restart. is this normal?
Service:
public class MyService extends Service
{
private static Timer timer = new Timer();
public void onCreate()
{
super.onCreate();
}
private void startService()
{
timer.scheduleAtFixedRate(new mainTask(), 0, 10000);
}
private class mainTask extends TimerTask
{
public void run()
{
//MY CODE
}
}
public int onStartCommand(Intent intent, int flags, int startId)
{
startService();
return START_STICKY;
}
public void onDestroy()
{
super.onDestroy();
}
public IBinder onBind(Intent intent)
{
return null;
}
}