How to manage Loopers and Threads (thread doesn't die anymore!)
Asked Answered
A

5

9

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 ;)

Acrolein answered 8/6, 2011 at 11:22 Comment(0)
B
20

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();
    }
}
Baywood answered 8/6, 2011 at 11:59 Comment(7)
In my case "handler" is a Handler object passed to this thread from the calling thread, to be used to pass the collected data from one to another. Thus, I cannot call handler.getLooper().quit() because it refers to the activity thread, and not to the userLocationThread. Note that onLocationChanged is inside the new thread, and that Looper is required to let this callback method to be called when the locationManager receives a location.Acrolein
Thanks; that does the work. By the way, what's the difference between handler.getLooper().quit() and Looper.myLooper().quit()? I thought they work the same!Acrolein
When you call Looper.myLooper() you get Looper associated with current thread. When you call handler.getLooper() you get Looper associated with specific handler.Baywood
But if the handler is associated with the same thread, the returned Looper should be the same, doesn't it?Acrolein
Yes, if you create handler in the same thread where you call Looper.myLooper(), they should be the same.Baywood
But in this case Looper.myLooper().quit() does not work, as handler.getLooper().quit() does instead o_OAcrolein
Oh, I see what you mean :) No, in your original code you call Looper.myLooper().quit() after you call Looper.mylooper().loop(). The loop() function does not return unless you call quit(). And you call quit() after loop(). So basically, you never reach your quit() and end up in infinite loop():)Baywood
V
0

"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.

Volcanic answered 18/9, 2014 at 21:3 Comment(0)
R
0

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.

Rollandrollaway answered 14/11, 2014 at 12:35 Comment(0)
R
0

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.

Rosemaryrosemond answered 30/1, 2017 at 18:45 Comment(0)
V
-1

Extend the AsyncTask class. It does all the threading and handling for you automatically.

Vintner answered 8/6, 2011 at 11:47 Comment(1)
I don't thinks AsyncTask will work in this case as thread has to have Looper loop.Baywood

© 2022 - 2024 — McMap. All rights reserved.