Hi Bob, Thanks a lot for this. This helped me to understand a lot of things. I found out a good way for starting/stopping a service here: http://www.brighthub.com/mobile/google-android/articles/34861.aspx
What do you think of this one ? Cheers, Tejas On Apr 18, 7:03 pm, Bob Kerns <[email protected]> wrote: > OK, I understand your thinking a bit better, so hopefully I can > explain a bit better. > > onDestroy() is only called when your application is idle. The system > will NEVER directly call a lifecycle method except when the main > thread is idle If you are running some long-lived function from, say, > onStart(), or a handler in the main thread. All of these are handled > one at a time by the main thread's Looper. > > So there will be no abrupt termination of your function when > onDestroy() is called. That only happens if it kills off the process. > Which is a lot more likely to happen if you block the main thread -- > after a while, the user will get a dialog offering to kill or wait for > the busy process! > > Java used to support interrupting and abruptly terminating threads. > However, there is absolutely NO way to make this a safe operation. > Android does kill off processes, which can leave things outside the > process in an inconsistent state, but that's not as bad as having your > application still running, but in an inconsistent state internally! So > abruptly force-quitting a method is not part of the model of any Java > system. (But you can still check, and throw an exception to get out of > a loop -- that will properly invoke all finally() clauses on the way > out, etc.). > > It's hard for me to tell you the "proper" way to stop your service, > because it depends on just what you're doing. You have part of the > idea, by checking for a termination flag in your loop. You can set > that in your onDestroy() method -- akin to what you're trying to do > with your call to the undocumented Looper.quit() method. But what you > want to do is to set your CARuntimes.MainServiceRunFlag instead. > (Except I'd make that an instance member of your Service class, rather > than a static of some other class. Better modularity, though you can > make it work this way). > > It looks to me like what you're really trying to do, is what the > system already provides for you better -- IntentService. I really > don't know what their intent was, calling it this -- I think I'd call > it ThreadedService, since the relevant point is that it runs your code > in a separate thread. Or maybe HandlerThreadService, since it uses a > handler to serialize the requests for work. You still have to be > careful about things that queue up things to the current thread's > looper -- like Toast, for example. Any deferred processing you want to > queue up to a handler you set up in the IntentService's onCreate > method (i.e. on the main thread). > > Anyway, the purpose of HandlerThread's and Handler's is not clean > shutdown, but rather the queuing of messages via a Looper and > associated MessageQueue. If you're only handling one message, ever, > then creating your own thread would make sense. An AsyncTask would be > a good choice if you're only running for a short time (but too long > for the main thread). But if this is something that runs for an > extended period of time, it would potentially block other uses of > AsyncTask, so your own thread is a better choice. > > So anyway, to summarize -- Handler's, HandlerThread's, and > IntentService give you a way to do one thing at a time in another > thread. AsyncTasks give you a way to do a small number of small things > in parallel (in some versions of the system, one thing at a time). > Long-lived things need their own threads -- HandlerThread and > IntentService can provide this. You stop a thread by testing for some > condition in the loop in the thread. If the thread needs to wait, use > wait(), and use notifyAll() to wake it up after you set the flag. Be > sure to use the synchronized keyword to coordinate in this case! And > if you loop within a handler, you're basically occupying that thread > while you're looping. So be careful not to queue up any additional > work to that thread's looper. (And often it needs to go to the main > thread anyway -- for example, all GUI operations need to be run > there). > > Sorry I can't make all that simple! Threading is never really simple. > But there are simple patterns, and if you carefully stay within those > simple patterns, you can avoid most of the complexity. > > Finally, about getApplicationContext() -- unfortunately, in the > released versions of the documentation, there are some bad examples > around this. I understand they've been fixed for the next release of > the SDK. But if you're in an Activity or a Service, you just supply > that activity or service, via the "this" pseudo-variable. However, in > your case, your use isn't directly in the service, but in your nested > ServiceHandler class, so "this" will refer to that instance instead. > But you can still refer to the outer instance's this, by writing > "ManagerService.this". You could also arrange to pass the context in > ass a parameter when you create your ManagerService -- but since the > system already does that work for you with a nested class, there's no > reason. Just replace getApplicationContext() with ManagerService.this, > and you're set. > > . > On Apr 18, 11:08 am, Tejas <[email protected]> wrote: > > > > > > If you're going to do that, why involve a handler > > > at all? > > > Now suppose, I use any other function in my service class instead of > > handler and say I'm performing a time consuming task in that function. > > If the onDestroy method of the service is called, I assume that it > > will terminate my function abruptly. I'm not sure of this, but I > > thought so and hence used the Handler thread to have a proper > > shutdown. please correct me if I'm wrong. Also let me know the right > > way to stop the service. > > > > Finally, never, ever, call getApplicationContext(). The return value > > > is not useful to us mortals. Supply the service itself as the context. > > > I'm still not sure what are you saying. How will you supply the > > service as the context ? Can you provide an example ? > > > On Apr 18, 2:56 am, Bob Kerns <[email protected]> wrote: > > > > Actually, just because something is a background service does NOT mean > > > it is running in a different thread. > > > > Background services run in the main thread. I suspect that this point > > > of confusion may be involved in your problem, though I don't quite > > > spot the problem. > > > > Also, you have a handler there that basically loops forever. That's > > > kind of an oxymoron. If you're going to do that, why involve a handler > > > at all? If I parse it correctly, it's not the main thread it'll be > > > blocking, but still... if something in that thread is posting via that > > > thread's looper rather than the context, that won't be processed. > > > > Finally, never, ever, call getApplicationContext(). The return value > > > is not useful to us mortals. Supply the service itself as the context. > > > To make things maximally confusing, getApplicationContext() will > > > appear to work in most circumstances. But it's always the wrong way to > > > get a context to use. > > > > On Apr 17, 2:05 pm, Tejas <[email protected]> wrote: > > > > > Ah... I got this working. Still I'm not sure of the reason for this > > > > (some thread issue I suppose) It would be great if someone can throw > > > > some light on this. > > > > > I was instantiating the sensor class in a background service.So the > > > > thread in which it was running was different than the main thread. > > > > I was doing something like this: > > > > > public class ManagerService extends Service { > > > > > private final String LTAG = this.getClass().getName(); > > > > private volatile Looper mServiceLooper; > > > > private volatile ServiceHandler mServiceHandler; > > > > > private final class ServiceHandler extends Handler{ > > > > public ServiceHandler(Looper myLooper) { > > > > super(myLooper); > > > > } > > > > > public void handleMessage(Message msg) { > > > > Log.v(LTAG, "handleMessage Called"); > > > > super.handleMessage(msg); > > > > > // Class Instantiation > > > > GPSSensor gs = new GPSSensor(); > > > > gs.setContext(getApplicationContext()); > > > > gs.startSensing() > > > > > // Main service loop > > > > while(CARuntimes.MainServiceRunFlag == true){ > > > > Log.v(LTAG, "In service Loop"); > > > > // Do something > > > > > SystemClock.sleep(60000); > > > > > }//while > > > > > } > > > > } > > > > > public void onCreate() { > > > > super.onCreate(); > > > > HandlerThread myThread = new HandlerThread("Main > > > > Service Thread"); > > > > myThread.start(); > > > > > mServiceLooper = myThread.getLooper(); > > > > mServiceHandler = new ServiceHandler(mServiceLooper); > > > > } > > > > > public void onStart(Intent intent, int startId) { > > > > super.onStart(intent, startId); > > > > > Message msg = mServiceHandler.obtainMessage(); > > > > //msg.obj = blah blah > > > > mServiceHandler.sendMessage(msg); > > > > > } > > > > > public void onDestroy() { > > > > Log.v(LTAG, "onDestroy called, quitting looper"); > > > > super.onDestroy(); > > > > > mServiceLooper.quit(); > > > > } > > > > > public IBinder onBind(Intent arg0) { > > > > return > > ... > > read more » -- You received this message because you are subscribed to the Google Groups "Android Developers" group. To post to this group, send email to [email protected] To unsubscribe from this group, send email to [email protected] For more options, visit this group at http://groups.google.com/group/android-developers?hl=en

