I have a thread that attempts to get the user location.
When the location is received "handler.sendMessage(msg)" is called, and it returns true, but handleMessage is never called.
There are no errors or warnings in logcat.
The code:
public class LocationThread extends Thread implements LocationListener {
// ... Other (non-relevant) methods
#Override
public void run() {
super.run();
Looper.prepare();
mainHandler = new Handler(Looper.myLooper()) {
#Override
public void handleMessage(Message msg) {
// This method is never called
}
};
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 0, 0, this);
Looper.loop();
}
#Override
public void onLocationChanged(Location location) {
// SendMessage is executed and returns true
mainHandler.sendMessage(msg);
if (mainHandler != null) {
mainHandler.getLooper().quit();
}
locationManager.removeUpdates(this);
}
}
Most likely this is happening because you are calling Looper.quit() immediately after posting the message to the Handler. This effectively terminates the message queue operation before the Handler has a chance to process it. Sending a message to the Handler simply posts it to the message queue. The handler will retrieve the message on the next iteration of the Looper. If your goal is to terminate the thread after a location update is received, it would probably be better to call Looper.quit() from inside handleMessage().
Editorial
Furthermore, if the only purpose for standing up this thread is to wait for the location update to come in, it's unnecessary. LocationManager.requestLocationUpdates() is an inherently asynchronous process (your main thread isn't blocked while the location fix is obtained). You can safely have your Activity/Service implement LocationListener directly and receive the location value there.
HTH
Related
I'm a little confused about something. Basically, I'm spawning a thread, and in addition I want to run a message loop in that thread. I'm basically doing the following:
This is straight out of the Android Looper class API documentation. However, my application always gets stuck at Looper.loop() and never returns from it. My current work around for this, is to just create a handler in the main thread (or UI thread) and send messages to that thread instead. However, for the sake of cleanliness and to just make the flow of my application make sense, I'd much rather send my messages to the thread I'm creating.
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
// The rest of the code below is a control loop
}
}
Any thoughts on what might cause Looper.loop() to never return?
Looper.loop creates an infinite loop and only stops when you call quit
http://developer.android.com/reference/android/os/Looper.html#loop()
This may work
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
while(true){
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
}
// The rest of the code below is a control loop
}
}
Scenario
Step 1: Init the location manager to read GPS locations every 50 meters:
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 50, locationListenerGps);
Step 2: each time a location is read:
#Override
public void onLocationChanged(Location location) {
new Thread(new Runnable() {
#Override
public void run() {
sendLocation(location);
}
}).start();
}
Step 3: on sendLocation there are a few things I do:
query the local sqlite database for failed to send records
if any, send them together together with the current location to a web service
if none, send only the current location
if sending failed (mostly because of data connectivity), insert location in database for future readings
if sending succeed, delete all rows from the database
The problem
All this is done in background in a service. For each sendLocation call I make a new thread. While connectivity is ok, everything works fine. But, when sending fails and the user is driving, the location read happens very often and there are big chances that there are 2-3 threads all trying to send the same unsent locations. If Thread1 receives the list and tries to send it, Thread2 and Thread3 should not be able to read it and try to send it as Thread1 may send it successfully. How can I prevent this from happening ? How can I make sure Thread2 does not read the list ?
From what I am thinking now, I could add a new field in the table "processing" and for all the rows retrieved for sending, update the field to true. In this case Thread2 will only get the processing=false rows. Is this a solution ? Any other recommendations ? I still believe that there is a slight change for Thread2 to get the data, while Thread1 is updating processing... Thanks.
Later edit: Extra thoughts and ideas I have tried this approach
private ExecutorService threadPool;
#Override
public void onCreate() {
scheduleTaskExecutor = Executors.newSingleThreadScheduledExecutor();
threadPool = Executors.newSingleThreadExecutor();
//also need to send location every 60 seconds if no other location was read
scheduleTaskExecutor.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
sendLocation(lastLocation);
}
}, 60, 60, TimeUnit.SECONDS);
}
#Override
public void onLocationChanged(Location location) {
threadPool.execute(new Runnable() {
#Override
public void run() {
sendLocation(location);
}
});
}
#Override
public void onDestroy() {
threadPool.shutdownNow();
}
From what I read, this threadPool should force threads to execute one after another, right ? (even I do have a feeling I misunderstood its purpose) If so, what happens if I get no connectivity for an hour ? For each location read, a new thread is added... but how long does this thread last ? I am concern what happens if the user is driving really fast, I could get locations read every 1-2 seconds, would this mechanism hold my web access in a queue, one thread after another ?
In another order of thoughts, what if onCreate method of the service I make a new thread. Something like:
#Override
public void onCreate() {
new Thread(new Runnable() {
#Override
public void run() {
startLocationListener();
}
}).start();
}
and in startLocationListener() I start GPS location read. Will onLocationChanged be executed on this thread and won't interfere with my UI thread ?
Would it be wiser to use a Service that runs in its own thread ? So I won't have to worry about threading ?
Using the current approach, the app does the job but there is something wrong happening, randomly and can't figure out the reason: one of my activities binds to the service to receive updates, I carefully unbind it when the app gets onPause... but sometimes the service keeps running, as I can see its notification icon displayed. I will investigate this more, but I need to settle a strong/reliable way of handling location reading and sending.
Later later edit
How about this approach:
private ExecutorService scheduleTaskExecutor;
#Override
public void onCreate() {
scheduleTaskExecutor = Executors.newSingleThreadScheduledExecutor();
//also need to send location every 60 seconds if no other location was read
scheduleTaskExecutor.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
sendLocation(lastLocation);
}
}, 60, 60, TimeUnit.SECONDS);
}
#Override
public void onLocationChanged(Location location) {
scheduleTaskExecutor.submit(new Runnable() {
#Override
public void run() {
sendLocation(location);
}
});
}
#Override
public void onDestroy() {
scheduleTaskExecutor.shutdownNow();
}
So let me get this straight: you want to send locations one after the other from a background thread. A simple scheme to achieve this would be (similar to your edited code, but I don't see the reason for ScheduledExecutor):
private ExecutorService exec;
#Override
public void onCreate() {
exec = Executors.newSingleThreadExecutor();
}
#Override
public void onLocationChanged(Location location) {
exec.submit(new Runnable() {
#Override
public void run() {
sendLocation(location);
}
});
}
#Override
public void onDestroy() {
exec.shutdownNow();
}
What this does under the hood is basically to create a background thread and a queue of tasks. Every time a location is read a new task is put into the queue. The thread continuously polls the queue and executes the tasks in order.
For each sendLocation call I make a new thread.
Why?
But, when sending fails and the user is driving, the location read happens very often and there are big chances that there are 2-3 threads all trying to send the same unsent locations.
This is why I asked "Why?" above.
If Thread1 receives the list and tries to send it, Thread2 and Thread3 should not be able to read it and try to send it as Thread1 may send it successfully. How can I prevent this from happening ? How can I make sure Thread2 does not read the list ?
IMHO, by not having Thread2 and Thread3 in the first place. Use a single thread at a time, that sends all unsent data. That is probably a long-lived thread, working off of a work queue (coupled with some sort of timer mechanism to handle the case where you failed to update before and wish to make sure you try again after X period of time, if no other events forced you to try sooner than that). I don't see why you would need more than that to achieve your aims.
In my code i'm using an IntentService to listen to location updates (either GPS or network updates) and this IntentService is triggered when an event is received, so it is started with startService() from any activity.
public class AddLocationService extends IntentService implements LocationListener {
/*My code here*/
}
#Override
protected void onHandleIntent(Intent intent) {
if(getOldLoc() == null)
{
//Get a new location
this.locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, TIME_INTERVAL_GPS, 0, this);
this.locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, TIME_INTERVAL_GPS, 0, this);
Log.d(AddLocationService.TAG, "Network listener started");
this.time_start_listening = System.currentTimeMillis();
mTimerThread mTimerRunnable = new mTimerThread();
this.timerThread = new Thread(mTimerRunnable);
this.timerThread.start();
}
else
/*REUSE OLD LOCATION*/
}
Now my problem is : When two events start this IntentService and the second starts it while the first one is still requesting for updates, I will like the second one to wait until first one is fully finished (location found OR timer thread finishes).
However whenever the IntentService is executed a second time (first instance still running), it prints me the log and does as it was executing in parallel.
However I thought that the main goal of IntentService was that it is something sequential so a second intent would have to wait until first one is done...
Did I missunderstood something ?
It appears that your onHandleIntent method is not blocking the thread it is executing on, so it will return quickly and allow the second intent to be processed. Not only that, but any callbacks from the LocationManager to that thread are unlikely to be processed as the background thread is likely to be killed when onHandleIntent is finished.
If you really want to use IntentService to manage your intent queue then you will need to do your location handling on its own thread, and join the IntentService thread to the location thread whilst it is waiting for the location callback.
Heres a bit of code that demonstrates the idea:
public class TestService extends IntentService {
private static final String TAG = "TestService";
private Location mLocation = null;
public TestService() {
super(TAG);
}
#Override
public void onHandleIntent(Intent intent) {
Log.d(TAG, "onHandleIntent");
if (mLocation == null) {
Log.d(TAG, "launching location thread");
LocationManager locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
LocationThread thread = new LocationThread(locationManager);
thread.start();
try {
thread.join(10000);
} catch (InterruptedException e) {
Log.d(TAG, "timeout");
return;
}
Log.d(TAG, "join finished, loc="+mLocation.toString());
} else {
Log.d(TAG, "using existing loc="+mLocation.toString());
}
}
private class LocationThread extends Thread implements LocationListener {
private LocationManager locationManager = null;
public LocationThread(LocationManager locationManager) {
super("UploaderService-Uploader");
this.locationManager = locationManager;
}
#Override
public void run() {
Log.d(TAG, "Thread.run");
Looper.prepare();
this.locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this);
this.locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
Looper.loop();
}
#Override
public void onLocationChanged(Location location) {
// TODO Auto-generated method stub
Log.d(TAG, "onLocationChanged("+location.toString()+")");
mLocation = location;
Looper.myLooper().quit();
}
#Override
public void onProviderDisabled(String arg0) {
}
#Override
public void onProviderEnabled(String arg0) {
}
#Override
public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
}
}
}
Of interest in there is the Looper that runs a message loop on the thread (to allow handling of the callbacks).
Given the effort required to do this with IntentService it might be worthwhile investigating deriving from Service instead and managing your own intent queue.
onHandleIntent is in it's own thread already. You don't (shouldn't) create on in there. It's all handled by IntentService for you.
Thanks a million, that is exactly what I needed to handle the location requests.
Thank you for explanations and making it clear for me, I wasn't very familiar with all the looper concept, now I understand it better !
In case someone need the same kind of thing, don't forget to stop the thread looper if your location thread is not stopping naturally (end of time on join(millis)), by adding this in onHandleIntent() :
if(thread.isAlive())
{
thread.onThreadStop();
try{
thread.interrupt();
}catch (Exception e) {
Log.d(TAG, "Exception on interrupt: " + e.getMessage());
}
}
after thread.join(yourTime), so for example if you didn't find any location update you still stop the thread after a certain time. And on method onThreadStop() :
/*We remove location updates here and stop the looper*/
public void onThreadStop()
{
this.locationManager1.removeUpdates(this);
handleLocationChange(AddLocationService.this.currentBestLocation);
Looper.myLooper().quit();
}
However I thought I saw my two intents being treated the first time I ran this code, but now only the first one is treated when I have multiple intents while still requesting location updates.
My method onHandleIntent() seems to execute correctly, stops the thread after the time specified and even displays the very last Log (last statement of the method) but the second intent is not executed...
Would you have any idea why ?
I created a class extending Thread to retrieve user location through LocationManager in a non-ui thread. I implemented this as a thread because it has to be started on request and do its work just for a limited time.
By the way, I had to add a Looper object in the thread, to be able to create the handler for the LocationManager (onLocationChanged).
This is the code:
public class UserLocationThread extends Thread implements LocationListener {
//...
public void run() {
try {
Looper.prepare();
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this);
Looper.loop();
Looper.myLooper().quit();
} catch (Exception e) {
//...
}
}
#Override
public void onLocationChanged(Location location) {
locationManager.removeUpdates(this);
//...
handler.sendMessage(msg); //this is the handler for communication with father thread
}
//...}
I would like the thread to start, receive the user location data (in this case just one time), send the data to the main thread via a message to the handler, and then die.
The problem is that in my case the thread does not die anymore, once the run method ended (that should be fine, because otherwise onLocationChanged would not receive the new locations).
But in this way, assuming that thread's stop and suspend methods are deprecated, what would be a good way, in this case at least, to make a thread with a looper die?
Thanks in advance ;)
You can explicitly quit from Looper's loop using Handler:
private Handler mUserLocationHandler = null;
private Handler handler = null;
public class UserLocationThread extends Thread implements LocationListener {
public void run() {
try {
Looper.prepare();
mUserLocationHandler = new Handler();
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this);
Looper.loop();
} catch (Exception e) {
//...
}
}
#Override
public void onLocationChanged(Location location) {
locationManager.removeUpdates(this);
//...
handler.sendMessage(msg);
if(mUserLocationHandler != null){
mUserLocationHandler.getLooper().quit();
}
}
"I implemented this as a tread because it has to be started on request and do its work just for a limited time."
This sounds like a perfect reason to simply reuse the main looper. There's no need to spawn a new Thread here. If you're doing blocking work (network I/O, etc) in onLocationChanged(), at that point you could spin up an ASyncTask.
Implement LocationListener on your Activity/Service or whatever and let it use the main looper by default.
Spawning a new thread, setting it to loop, and then immediately quitting is unnecessary.
IntentService is good for do this job.
IntentService is a base class for Services that handle asynchronous requests (expressed as Intents) on demand. Clients send requests through startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.
Looper().quit(); is good, and according to specification:
Causes the loop() method to terminate without processing any more messages in the message queue.
But, if you have a task that already is under processing, and you want to stop it too, you can acquire working thread and cause it to interrupt:
#Override
public void onLocationChanged(Location location) {
locationManager.removeUpdates(this);
handler.sendMessage(msg); //this is the handler for communication with father thread
if(mUserLocationHandler != null){
mUserLocationHandler.getLooper().quit();
mUserLocationHandler.getLooper().getThread().interrupt(); // <-- here
}
}
This works fine with most IO, and thread locking/waiting.
Extend the AsyncTask class. It does all the threading and handling for you automatically.
I've more than one Handlers in an Activity. I create all the handlers in the onCreate() of the main activity. My understanding is the handleMessage() method of each handler will never be called at the same time because all messages are put in the same queue (the Activity thread MessageQueue). Therefore, they will be executed in the order in which are put into the Queue. They will also be executed in the main activity thread. Is this correct ?
public void onCreate() {
this.handler1 = new Handler() {
#Override
public void handleMessage(Message msg) {
//operation 1 : some operation with instanceVariable1
super.handleMessage(msg);
}
};
this.handler2 = new Handler() {
#Override
public void handleMessage(Message msg) {
//Operation 2: some operation with instanceVariable1
super.handleMessage(msg);
}
};
this.handler3 = new Handler() {
#Override
public void handleMessage(Message msg) {
//Operation 3: some operation with instanceVariable1
super.handleMessage(msg);
}
};
}
From the docs "When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue."
So you're right, they will run in the order that you queue them on the UI thread (since you are going to create them in onCreate).
One message per time, and per thread, and per handler.
Every new Handler(...) instance is bound, explicitly or implicitly, to Looper instance, and only once.
Looper instance, is already created somewhere with Looper.prepare() call
//usually obtained by Activity.getMainLooper() or Looper.myLooper()
Looper.prepare() uses ThreadLocal variable sThreadLocal (static field) to have one Looper instance per thread.
( It works same like hashMap.put(Thread.getCurrent(), new Looper()) )
every looper has its own private MessageQueue
every Looper instance has its main method loop()
loop(){
while(true){
Message msg = messageQueue.next();
msg.target.dispatchMessage(msg);
}
}
every message have a (Handler) target is set, and exception is thrown(within MessageQueue.enqueueMessage()) if it does not.
since Handler cannot be bound to several Loopers is does so every handler receives only one message at time and only with msg.target==handler
so sendMessage() or postMessage() works something like this:
handler.post(Message msg){
Looper.sThreadLocal.get(Thread.getCurrent()).messageQueue.push(msg);
}
so call stack , while handle message, should look something like this:
Looper.myLooper()-> Thread.getCurrent()-> Looper-> MessageQueue.next()-> Message-> Message.target-> Handler-> dispatchMessage()-> handleMessage()