I have following problem. I have this implementation of my Thread with Looper.
public class GeoLocationThread extends Thread{
public Handler handler;
private General general;
public void run(){
Looper.prepare();
handler = new IncomingHandler(general);
Looper.loop();
}
public GeoLocationThread(General general){
this.general=general;
}
private static class IncomingHandler extends Handler{
private final WeakReference<General> mService;
IncomingHandler(General service) {
mService = new WeakReference<General>(service);
}
#Override
public void handleMessage(Message msg)
{
General service = mService.get();
if (service != null) {
Location location=service.getLl().getLocation();
if(location.getAccuracy()<40){
service.setOrigin(new GeoPoint((int) (location.getLatitude() * 1E6),(int) (location.getLongitude() * 1E6)));
}
}
}
}
}
and i would like to do the following:
GeoLocationThread locationThread=new GeoLocationThread(this);
locationThread.start();
lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, ll, locationThread.handler.getLooper());
Where lm is LocationManager. From my log and testing I am able to say that locationThread.handler.getLooper() returns null instead of the Looper.
I don't know why it is null. I have tried to call locationThread.isAlive() which has returned true. I have also tried to get locationThread.handler; which I know is not null.
I have also done the lot of googling, but I haven't found more than the documentaion.
Thank you very much in advance for your answers.
Your code is reading null most likely because the operations two are not synchronous with each other. You cannot successfully call getLooper() on a Handler until Looper.prepare() is finished and the Handler is constructed. Because Thread.start() does not block while the other thread executes (of course, why would it? that would defeat the purpose of the new Thread) you have created a race condition between the run() block of the Thread and the code trying to set up the location listener. This will produce different results on different devices based on who can execute first.
Furthermore, registering location updates is already an asynchronous process, so one wonders why the secondary thread is needed? You can simply request updates to your listener without passing in a secondary Looper and the listener will get data posted when new updates are available, the main thread does not stay blocks during this process.
Do you have to call super() in your constructor? Maybe the Looper is not getting set because the parent constructor is not being called?
Ok, try this. Make this:
public class GeoLocationThread extends Thread{
be this:
public class GeoLocationThread extends HandlerThread{
then you can do this.getLooper() when you construct the Handler or when you need the looper.
Related
I am trying to make use of HandlerThread in android and am ending up with either a situation in which the UI thread is not responding anymore, or a strange IllegalStateException. I want to give you a minimal example of my problem.
I have a class DataManager which instantiates a worker thread on creation:
public class DataManager
{
private final HandlerThread loaderThread = new HandlerThread( "Worker" );
private final Producer loader;
Inside of this class I have my Handler defined:
private static class Producer extends Handler
{
public Producer( Looper looper )
{
super( looper );
}
#Override
public void handleMessage( Message msg )
{
msg.recycle();
}
}
The constructor of my DataManager runs the worker thread and associates the handler with the thread's looper:
public DataManager()
{
loaderThread.start();
this.loader = new Producer( loaderThread.getLooper() );
}
Before DataManager is destroyed, it stops the thread and waits for it to finish. Actually I believe this part is not relevant to my problem, because my DataManager instance is definitely alive all the time:
#Override
protected void finalize() throws Throwable
{
loaderThread.quit();
loaderThread.join();
super.finalize();
}
Finally, I have doSomething method, which simply posts a message to the worker thread:
public void doSomething()
{
Message msg = Message.obtain();
loader.sendMessage( msg );
}
Now I'm instantiating the DataManager from inside of a custom view on the UI thread. When the view is about to paint itself using onDraw it calls doSomething on the DataManager. The further behavior depends on whether an AsyncTask is currently running in background or not:
If it is running, than the UI thread is becoming unresponsive form this moment on.
Otherwise, I get an IllegalStateException, thrown from within a subroutine of Looper.loop of the UI thread, saying:
IllegalStateException: The specified message queue synchronization barrier token has not been posted or has already been removed.
Google gives absolutely no results for this message. I've been reading documentation and searching for similar problems for a few hours now and still have no idea what I might be doing wrong. Any ideas?
Got it. Obsiously the situation about recycling messages is this:
If you send it to a handler, the handler/looper will recycle it for you.
So one must not recycle the message within handleMessage.
Folks,
Here is a simplified code for my background thread:
public class MyThread extends Thread {
private Handler _handler;
public void run() {
Looper.prepare();
this._handler = new Handler();
Looper.loop();
}
public void DoSomething() {
if (!this.isAlive()) {
this.start();
}
this._handler.post(blah);
}
}
The problem I have is that the background thread may not have yet created the handler object when post() call is made. Essentially, I need a wait loop for the handler object to be initialized. What is generated accepted method of doing this under Android?
Thank you in advance for your help.
Regards,
Peter
You can set a flag after you initialize the Handler and wait for this flag before calling post.
An easy way to wait for a flag in a concurrent system is with a CountDownLatch. It would start at 1 and decrement after the Handler is initialized. Check out the details here:
http://download.oracle.com/javase/1,5,0/docs/api/java/util/concurrent/CountDownLatch.html
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'm looking at the ListActivity source code, and I'm seeing that a private Handler is being defined, and that a Runnable is posted to this handler in the onContentChanged() method.
I don't quite get the point of this, as the handlers, as I understand it, are there for inter-thread communication. Here, the definition of the handler and the posting is happening on the same thread, and no delay is specified in the post() call. I can't see the handler being used for anything else, either.
I've probably misunderstood something about the use of handlers here. Why is it done the way it is here, and not by just running mList.focusableViewAvailable() (the call inside the runnable) directly? Wouldn't the result be the same?
Beneath is what I believe are the relevant portions of the ListActivity source code:
public class ListActivity extends Activity {
protected ListView mList;
private Handler mHandler = new Handler();
private Runnable mRequestFocus = new Runnable() {
public void run() {
mList.focusableViewAvailable(mList);
}
};
/**
* Updates the screen state (current list and other views) when the
* content changes.
*
* #see Activity#onContentChanged()
*/
#Override
public void onContentChanged() {
super.onContentChanged();
View emptyView = findViewById(com.android.internal.R.id.empty);
mList = (ListView)findViewById(com.android.internal.R.id.list);
if (mList == null) {
throw new RuntimeException(
"Your content must have a ListView whose id attribute is " +
"'android.R.id.list'");
}
if (emptyView != null) {
mList.setEmptyView(emptyView);
}
mList.setOnItemClickListener(mOnClickListener);
if (mFinishedStart) {
setListAdapter(mAdapter);
}
mHandler.post(mRequestFocus);
mFinishedStart = true;
}
}
Why is it done the way it is here, and not by just running mList.focusableViewAvailable() (the call inside the runnable) directly? Wouldn't the result be the same?
Your concern should not be the Handler. Your concern should be the call to post(). A Handler is not even really needed, as post() is available on View -- this code may pre-date that, though.
post() takes a Runnable and puts it on the message queue for the main application thread. As such, it will not get processed until all other messages that are presently on that queue get processed (FIFO). Presumably, ListActivity needs some other message on the queue to be processed first before focusableViewAvailable() will work successfully.
I have an app with a two threads - main and data loader. When data loader finishes it posts a Runnable object to the main thread (as described in the DevGuide), but it never gets delivered and run.
Here's the basic code:
class MyApp extends Application
{
public void onCreate()
{
LoaderThread t = new LoaderThread();
t.start();
}
private class LoaderThread extends Thread
{
public void run()
{
SystemClock.sleep(2000);
boolean res = m_handler.post(m_runnable);
if(res)
Log.d(TAG, "Posted Runnable");
}
}
private final Handler m_handler = new Handler();
private final Runnable m_runnable = new Runnable() {
public void run()
{
Log.d(TAG, "Hey, i'm runnable!");
}
}
}
Also it maybe important to note that I ran this code as a unit-test derived from an ApplicationTestCase:
class MyAppTest : public ApplicationTestCase
{
public MyAppTest()
{
super(MyApp.class);
}
public void testLoading()
{
createApplication();
// few asserts follow here...
}
}
So this fails. Runnable never gets run() called, although the log indicates that it has been posted successfully.
I also tried to send simple messages instead of posting runnable (m_handler.sendEmptyMessage(1) for example) - they never get delivered to handler's callback in the main thread.
What do I miss here?
Thanks in advance :)
A Handler requires a Looper in order to work. The Looper provides a message queue required by the Handler.
All instances of Activity have a Looper as one is used to process UI Events, but you can create your instance of Looper elsewhere.
Have a look in your Log output to see if Android is complaining about the absence of a Looper.
If it is, you might be able to fix by add the following to the top of your onCreate() method:
Looper.prepare();
m_handler = new Handler();
Looper.run();
And remove the initialisation of m_handler from later in your code.
Handler only works in an Activity AFAIK. You are attempting to use it in an Application.
An alternative to calling Looper.prepare() is to call new Handler(Looper.getMainLooper()). The problem with calling Looper.prepare() is that it will throw an exception when there is already a looper on your thread. Chances are you are writing code that has to run under different environments and this solution will handle more cases.
See:
AsyncTask and Looper.prepare() error