How to properly restart an Android service at exception? - android

I am trying to develop an Android Service that can automatically restart at exceptions.
I have tried adding return START_STICKY; on the onStartCommand method. But since the exception will not cause the service to crash, it won't automatically restart.
I have also tried the methods mentioned in How to restart service in android to call service oncreate again, like putting together the following code, but after onDestory()was called, only onCreate() is executed but not onStartCommand()
stopService(new Intent(this, YourService.class));
startService(new Intent(this, YourService.class));
Right now, the service looks like this:
public class PostService extends Service {
site.bdsc.raspberry_gps_test.sim800Cutil.Sim800Manager Sim800Manager;
private Thread thTestPost;
private boolean mRunning;
private static String TAG = "PostService";
public PostService() {
}
#Override
public void onCreate(){
//get service
thTestPost = new Thread(testPost,"testPost");
Log.d(TAG,"Service on create");
}
#Override
public int onStartCommand(Intent intent,int flags,int startId){
if (!mRunning) {
// Prevent duplicate service
mRunning = true;
Log.d(TAG,"Starting Post Service");
try {
Sim800Manager = Sim800ManagerImpl.getService("UART0");
} catch (IOException e) {
restartService(); //want to restart service here
}
thTestPost.start();
}else{
Log.d(TAG,"Duplicated Start Request!");
}
return START_STICKY;
}
#Override
public void onDestroy(){
super.onDestroy();
Log.d(TAG,"Service on destory");
mRunning = false;
thTestPost.interrupt();
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
private Runnable testPost = new Runnable() {
#Override
public void run() {
// some code
}
};
private void restartService(){
stopService(new Intent(this, PostService.class));
startService(new Intent(this,PostService.class));
}
}
As shown in the code, I want the PostService to properly restart when IOException is caught.

Use this START_REDELIVER_INTENT
public static final int START_REDELIVER_INTENT
Constant to return from onStartCommand(Intent, int, int): if this
service's process is killed while it is started (after returning from
onStartCommand(Intent, int, int)), then it will be scheduled for a
restart and the last delivered Intent re-delivered to it again via
onStartCommand(Intent, int, int).

Related

Set a Listener in a Service-based Class

Hy i have a problem to set the ServiceUpdateUIListener in the service to update the UI. It's wrong to make a new Service object and set there the listener and put it in an intent.
Code source is at http://developerlife.com/tutorials/?p=356 there i can't find how the set the listener and start the service right.
Calling:
TimerService service = new TimerService();
TimerService.setUpdateListener(new ServiceUpdateUIListener() {
#Override
public void updateUI(String time) {
clock.setText(time);
}
});
Intent i = new Intent(Timer.this,service.class); //service cannot be resolved to a type
i.putExtra("ms", ms);
startService(i);
Service:
public class TimerService extends Service{
CountDownTimer timer;
Chronometer clock;
public static ServiceUpdateUIListener UI_UPDATE_LISTENER;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onStart(Intent intent, int startId) {
// TODO Auto-generated method stub
int ms = intent.getIntExtra("ms", 0);
timer = new CountDownTimer(ms,1000){
#Override
public void onTick(long millisUntilFinished) {
int seconds = (int) (millisUntilFinished / 1000) % 60 ;
int minutes = (int) ((millisUntilFinished / (1000*60)) % 60);
int hours = (int) ((millisUntilFinished / (1000*60*60)) % 24);
clock.setText( String.format("%02d:%02d:%02d", hours,minutes,seconds));
Log.e("Timer", String.valueOf(millisUntilFinished));
}
#Override
public void onFinish() {
// TODO Auto-generated method stub
}
}.start();
super.onStart(intent, startId);
}
public static void setUpdateListener(ServiceUpdateUIListener l) {
UI_UPDATE_LISTENER = l;
}
The Service documentation has fairly complete sample code for implementing a service in your app that another part of your app can bind to and make calls on:
http://developer.android.com/reference/android/app/Service.html#LocalServiceSample
Just put your setUpdateListener() method on the Service, and call it once you get onServiceConnected() with the service.
So your code would be something like this:
public interface UpdateListener {
public void onUpdate(long value);
}
class LocalService {
// Like in the Service sample code, plus:
public static String ACTION_START = "com.mypackage.START";
private final ArrayList<UpdateListener> mListeners
= new ArrayList<UpdateListener>();
private final Handler mHandler = new Handler();
private long mTick = 0;
private final Runnable mTickRunnable = new Runnable() {
public void run() {
mTick++;
sendUpdate(mTick);
mHandler.postDelayed(mTickRunnable, 1000);
}
}
public void registerListener(UpdateListener listener) {
mListeners.add(listener);
}
public void unregisterListener(UpdateListener listener) {
mListeners.remove(listener);
}
private void sendUpdate(long value) {
for (int i=mListeners.size()-1; i>=0; i--) {
mListeners.get(i).onUpdate(value);
}
}
public int onStartCommand(Intent intent, int flags, int startId) {
if (ACTION_START.equals(intent.getAction()) {
mTick = 0;
mHandler.removeCallbacks(mTickRunnable);
mHandler.post(mTickRunnable);
}
return START_STICKY;
}
public void onDestroy() {
mHandler.removeCallbacks(mTickRunnable);
}
Now you can start the service to get it to start counting, and anyone can bind to it to register a listener to receive callbacks as it counts.
It is really hard though to answer your question very well because you aren't really saying what you actually want to accomplish. There are a lot of ways to use services, either starting or binding or mixing the two together, depending on exactly what you want to accomplish.
Now you can implement your client code again based on the sample:
public class SomeActivity extends Activity implements UpdateListener {
private LocalService mBoundService;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mBoundService = ((LocalService.LocalBinder)service).getService();
mBoundService.registerListener(this);
}
public void onServiceDisconnected(ComponentName className) {
mBoundService = null;
}
};
void doBindService() {
bindService(new Intent(Binding.this,
LocalService.class), mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
void doUnbindService() {
if (mIsBound) {
if (mBoundService != null) {
mBoundService.unregisterListener(this);
}
unbindService(mConnection);
mIsBound = false;
}
}
protected void onDestroy() {
super.onDestroy();
doUnbindService();
}
I don't know exactly what you want, but this is not the way to do it. It seems you're mixing up a lot of things.
The tutorial itself is a bad example to my opinion, keeping a static reference to an activity in a service seems to me bad practice; you would use binding to bind your service to an activity, or if you don't want to you can pass Intents around.
As far as I know instantiating a service like you do and setting a listener on it like that doesn't work. You get an error in the startService() call because the service instance isn't a class obviously; you should use TimerService.class instead. In your service you have an onStart(); onStart() is a deprecated function, you should use onStartCommand() instead.
Now, if you have an activity in which you want to show a clock you don't need nor want the service to update its UI directly of course, but if you'd want the service to calculate a new clock tick for you, just call startService(); As long as your service is alive, sending a new start service intent will just call the onStartCommand() with the intent you're sending along.
If your clock is in an activity, setup a broadcast receiver inside your activity that and let your service broadcast an intent that can be received by the broadcast receiver you setup, with your new clock value passed along.
MrJre is correct that onStart is depreciated and that you should be using onStartCommand().
If you want to get this to work, there is a better way.
I am doing something similar, as in wanting to update a UI from results happening in a service. This was not particularly easy. (In my opinion)
Here's how to do it: (First off scrap your existing code)
In UI class add:
public Intent service;
service = new Intent(thisContext, TimerService.class);
service.putExtra("ms", ms);
startService(service);
//bind service to the UI **Important**
bindService();
IntentFilter timerFilter = new IntentFilter("TimerIntent"); // Filter that gets stuff from the service
registerReceiver(myReceiver, timerFilter);
void bindService() {
Intent newIntent = new Intent(this, TimerService.class);
bindService(newIntent, mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder binder) {
s = ((TimerService.MyBinder) binder).getService();
}
#Override
public void onServiceDisconnected(ComponentName className) {
s = null;
}
};
public void releaseBind() {
if (mIsBound) {
unbindService(mConnection);
mIsBound = false;
}
}
// Now in this class we need to add in the listener that will update the UI (the receiver registered above)
private BroadcastReceiver myReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
//Get Bundles
Bundle extras = intent.getExtras();
/* DO ANY UI UPDATING YOU WANT HERE (set text boxes, etc.) TAKING INFO FROM THE "extras" Bundle ie: setting the clock*/
//ie: int timerTest = extras.getInt("0");
// Now update screen with value from timerTest
}
};
Service File:
public class TimerService extends Service {
public TimerService () {
super();
}
private final IBinder mBinder = new MyBinder();
public Timer clockTimer = new Timer();
public int timer = 0;
// We return the binder class upon a call of bindService
#Override
public IBinder onBind(Intent arg0) {
return mBinder;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// After service starts this executes
Bundle extras;
extras = intent.getExtras();
/* Call a function to do stuff here. Like if you are a clock call a timer function updates every second */
// Here's an example, modify to fit your needs.
clock();
return START_STICKY;
}
public class MyBinder extends Binder {
TimerService getService() {
return TimerService.this;
}
}
public void clock() {
clockTimer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
try {
// Some function ie: Time = Time + 1 //
/* MAKE SURE YOU BROADCAST THE RECEIVER HERE. This is what you send back to the UI. IE:*/
timer = timer+ 1; // increment counter
Intent intent = new
//Bundle the timervalue with Intent
intent.putExtra("0", timer);
intent.setAction("TimerIntent");
sendBroadcast(intent); // finally broadcast to the UI
} catch(Exception ie) {
}
}
},
0, // Delay to start timer
1000); // how often this loop iterates in ms (so look runs every second)
}
There might be some syntax errors in this code as I've just modified my existing and working code to try and fit your needs. There will obviously need to also be some modifications depending on what you want to do. But follow this framework and you will be able to do what you are trying to do.
This works for me, so hopefully you can modify this to work for you. (Only thing I've left out are the imports, but you should be able to easily figure that out)
Key points:
Bind service to UI
Register listener in UI file to respond to the broadcast from inside the service.
Cheers.

What kind of services should I use for accessing sensors data in background?

I successfully used a service to do a certain task in the foreground. Now, to do it in the background, I'd remove the handler.removeCallbacks method in onDestroy().
But this would also prevents me from stopping the service using stopService(intent).
I saw on the official docs that I should maybe use JobScheduler (as I target API 28).
Here is a more precise indication of my code :
public class MainActivity {
private Intent intent;
onCreate() {
if (intent == null) {
intent = new Intent(this, MyService.class);
}
}
startService(intent);
... // Then is some code to stop the service if needed with stopService(intent)
}
--------------------------------------------------------------
public class myService {
private Handler handler = null;
private static Runnable runnable = null;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
handler = new Handler();
runnable = new Runnable() {
#Override
public void run() {
System.out.println("Running service times " + i);
i++;
handler.postDelayed(runnable, 1000);
}
};
handler.post(runnable);
}
#Override
public void onDestroy() {
handler.removeCallbacks(runnable);
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
I would like it to run in the background (even if the device is locked) but still being able to disable the service (or JobScheduler?).
What are your suggestions?
you can use work manager
or job dispatcher
and there is a lot of options like
SyncAdapter, Bound services, Intent Service
you can use one of these options according to your need

gracefully clean up bound service when activity is getting swiped from the recents

I have a bound service which goes to the foreground when needed.
Here's a simplified version of what I have:
class MyService extends Service {
private static final ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder serviceBinder) {
instance.bound();
}
#Override
public void onServiceDisconnected(ComponentName name) {
instance.unbound();
}
};
private static MyService instance;
public MyService() {
instance = this;
}
public static boolean bind(final Context context) {
final Intent intent = new Intent(context, MyService.class);
return context.bindService(intent, MyService.serviceConnection, Context.BIND_AUTO_CREATE);
}
public static void unbind(final Context context) {
context.unbindService(MyService.serviceConnection);
}
#Override
public void onTaskRemoved(Intent rootIntent) {
// not being called
}
#Override
public boolean onUnbind(Intent intent) {
// not being called
}
private void bound() {}
private void unbound() {
// not being called
}
}
Then in my activity I simply do this to bind:
MyService.bind(this);
and this to unbind:
MyService.unbind(this);
The problem is that it seems that I have no way of knowing when the user have swiped the activity off the recent.
Here's what I tried:
Use the activity onDestroy method: nope, not being called
Use the activity onPause/onStop methods: can't really distinguish between swipe case to just going to the background, as the isFinishing() results in false in all cases.
Use the service onTaskRemoved or onUnbind methods: not being called
In my AndroidManifest.xml adding android:stopWithTask="true" in the service element: that indeed kills my service when the activity is swiped, but it results in: MyActivity has leaked ServiceConnection. Not exactly sure why, but my guess is that I did not get the chance to call the unbindService().
What am I doing wrong or what am I missing?
Thanks.
As already mentioned by mklimek, onTaskRemoved() is the way to go for this.
I use it in a project with a bound service that is started and bound by an Activity. Here are the respective parts (I'll add a bit of context to be safe):
Activity calls custom startService() and bindService() helper methods from onCreate():
private void startService() {
Intent myServiceIntent = new Intent(this, packageName.MyService.class);
startService(myServiceIntent);
}
private void bindService() {
mServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mService = MyServiceListener.Stub.asInterface(iBinder);
try {
mService.registerCallback(myServiceCallback);
mService.doSomething();
} catch (RemoteException e) {
MLog.w(TAG, "Exception on callback registration; Service has probably crashed.");
}
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
mService = null;
}
};
if(!myServiceIsBound) {
Intent myServiceIntent = new Intent(this, packageName.MyService.class);
bindService(myServiceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
myServiceIsBound = true;
// service is now bound
} else {
// service has already been bound
}
}
Now, to the Service class: in it's onCreate(), I show a notification (which is afaik required for running background services) and set up the Binder:
#Override
public void onCreate() {
super.onCreate();
// Setup Binder for IPC
mBinder = new MyServiceBinder();
mNotificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
[...display notification code...]
}
Service interface (tell me if that's interesting, otherwise I just leave it out here):
private class MyServiceBinder extends MyServiceListener.Stub {
#Override
public void registerCallback(MyServiceCallback callback) throws RemoteException {
[...]
}
// further methods for the service interface...
}
My onTaskRemoved() and the other lifecycle methods look as follows:
#Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
// do something adequate here
}
// Lifecycle management
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_REDELIVER_INTENT;
}
// Binding
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
#Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
My onTaskRemoved() is called every time I swipe the Activity from the recent apps list. Are you sure your onTaskRemoved() method is not called (did you put some logging code there)? Also make sure to call the super.onTaskRemoved() method within it.
Our code looks quite similar, except that I put the ServiceConnection setup and Service binding code into the Activity. You moved a lot of this logic into the Service itself.
I can only guess that maybe here lies the problem with the leaking of the Service connection, as your ServiceConnection is a static member of your Service class, and you maintain a reference to your Service from within your ServiceConnection (via your "instance" variable). I'm not sure, but it seems both links are not broken, when the Activity is terminated. Mind, how in my code the ServiceConnection is a member of the Activity, and I clear the reference to mService in onServiceDisconnected() to be safe. Maybe you can refactor this a bit, taking references that cannot be GCed upon Activity termination into account.

Why does my service calls its onCreate() itself while running?

I have a Service in my android application which stop only when quitting the application.
public class MyService extends Service {
public MyService() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
try {
if(intent != null){
//......
}
} catch (Throwable e) {
}
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public void onCreate() {
super.onCreate();
//......
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
This service calls its onCreate() method sometimes itself while running.When checking the state of service it is running but its functionalities does not work.I am stuck on this for many hours.What is the problem here?I tried onLowMemory() in this service.But not result.
If you return START_STICKY in the onStartCommand(), the Service gets created again if its killed by system (probably on a low memory condition). The same thing must have happened in your case and onCreate() got called.

Sticky Service Management

I've got a Sticky Service (returns START_STICKY from onStartCommand) which executes some code in an AsyncTask, but I'm having some problems with how and when to start, bind, stop, unbind. I only want the service around whilst the parent activity is alive, I don't want it hanging around in the background when the app has been closed, but I need the service to survive an orientation change. I currently don't need the service to be active for the entire duration of the activity being active, so I call stopSelf() after the main work is done in my AsyncTask in the Service and then start the Service again when needed. Sometimes I'll need to interrupt the work the service is doing, cancel the AsyncTask and start again with different data. The problem is that no matter what I do - I can't seem to get it solid throughout all the different possible scenarios. Can anyone have a look through and tell me what I'm doing wrong?
My Service is :
public class ChordCalculatorService extends Service {
private final IBinder mBinder = new LocalBinder();
private AsyncTask<SearchData, SearchStatusData, List<Item>> currentTask;
#Override
public void onCreate() {}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
/**
* Class for clients to access. Because we know this service always runs in
* the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
public ChordCalculatorService getService() {
return ChordCalculatorService.this;
}
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public SearchData getSearchData() {
return searchData;
}
public void startWork() {
if (currentTask != null && currentTask.getStatus() == Status.RUNNING) {
currentTask.cancel(true);
}
if(searchData != null) {
Worker task = new Worker();
currentTask = task.execute(new SearchData[] { searchData });
} else {
Message msg = handler.obtainMessage(ERROR, "No search data set");
handler.sendMessage(msg);
}
}
class Worker extends AsyncTask<SearchData, SearchStatusData, List<Item>> {
// ... code ...
#Override
protected void onPostExecute(List<Item> result) {
Message msg = handler.obtainMessage(COMPLETE, new StatusData(Status.STATUS_FINISHED, result));
handler.sendMessage(msg);
stopSelf();
}
}
}
Currently I have the Service being started when my custom View is created:
public class MyCustomView extends BasicFretBoardView {
private ServiceConnection conn;
private MyService myService;
private boolean isServiceStarted;
private boolean isServiceBound;
public MyCustomView(Context context, AttributeSet attr) {
super(context, attr);
startService();
}
public void startService() {
Intent serviceIntent = new Intent(getContext(), MyService.class);
conn = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
myService = ((LocalBinder) service).getService();
myService.registerHandler(serviceHandler);
}
#Override
public void onServiceDisconnected(ComponentName name) {
myService = null;
}
};
// Explicitly start the service. Don't use BIND_AUTO_CREATE, since it
// causes an implicit service stop when the last binder is removed.
getContext().startService(serviceIntent);
getContext().bindService(serviceIntent, conn, 0);
isServiceStarted = true;
isServiceBound = true;
}
public void stopService() {
if (isServiceStarted) {
Intent serviceIntent = new Intent(getContext(), MyService.class);
getContext().stopService(serviceIntent);
isServiceStarted = false;
}
unBindService();
}
public void unBindService() {
if(isServiceBound) {
getContext().unbindService(conn);
isServiceBound = false;
}
}
// gets called based on some user interaction
private void startServiceWork() {
if(!isServiceStarted) {
startService();
} else {
myService.cancelCalcalation();
}
myService.setData(data);
myService.startWork();
}
}
and stopping the service is handled in the Activity:
public class CustomChordActivity extends Activity {
// ... code ...
#Override
public void onBackPressed() {
super.onBackPressed();
}
#Override
protected void onPause() {
if(isFinishing()) {
chordsView.stopService();
}
super.onPause();
}
#Override
protected void onStop() {
super.onStop();
}
#Override
protected void onDestroy() {
chordsView.unBindService();
super.onDestroy();
}
#Override
protected void finalize() throws Throwable {
super.finalize();
}
}
It seems that you want your task to run on demand, maybe an IntentService would be a more suitable option. When you need work to be done, (startServiceWork()), you just start the service and that kicks off your AsyncTask. The service will then finish after the task has finished.
Now, regarding orientation changes, you would have to implement a Broadcast Receiver whose intent filter is "android.intent.action.CONFIGURATION_CHANGED". (I assume that you want the service to do work when the orientation changes) Place the Broadcast Receiver, within your activity/main ui thread. This will in effect make the hosting process of your Broadcast Receiver to be the main application process making it safer to start the service from within the Broadcast Receiver.

Resources