How can I guarantee that another thread handler is not null before it is called?

My program threw a NullPointerException the other day when I tried to use a handler created in another thread to send a message to this thread. A handler created by another thread has not yet been created or is not yet visible to the calling thread, even though the calling thread has already called the start in another thread. This is very rare. Almost every test run does not receive an exception.

I was wondering how best to avoid this problem, with minimal complexity and a penalty for performance. The program is a game and is very sensitive to performance, especially after its launch. Therefore, I try to avoid using synchronization after installation, for example, and would prefer to avoid turning on a variable at any time.

Background:
On Android, the Handler class can be used to "trigger an action that must be executed on a thread other than your own." The documentation is here:
http://developer.android.com/intl/de/reference/android/os/Handler.html

A handler must be created in the stream where it will be used. Therefore, creating it in a thread constructor that is started by the thread creating this thread is not an option.

When the handler is for a thread other than the user interface thread, the Looper class should also be used:
http://developer.android.com/intl/de/reference/android/os/Looper.html

The documentation provides an example of using two classes for this purpose:

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

My very ugly workaround currently looks like this:

 public class LooperThread extends Thread { public volatile Handler mHandler; public final ArrayBlockingQueue<Object> setupComplete = new ArrayBlockingQueue<Object>(1); public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; setupComplete(); Looper.loop(); } public void waitForSetupComplete() { while ( true ) { try { setupComplete.take(); return; } catch (InterruptedException e) { //Ignore and try again. } } } private void setupComplete() { while( true ) { try { setupComplete.put(new Object()); return; } catch (InterruptedException e) { //Ignore and try again. } } } } 

With the code in thread creation, it looks like this:

  LooperThread otherThread = new LooperThread(); otherThread.start(); otherThread.waitForSetupComplete(); otherThread.mHandler.sendEmptyMessage(0); 

Are there any better solutions? Thanks.

+7
java android null multithreading
source share
3 answers

Preparing a Looper may block for a while, so I assume that you are in a state where prepare() takes a moment to complete, so mHandler is still undefined.

You can add Thread HandlerThread , although even then you still have to wait for Looper initialize. Perhaps something like this might work where you have a Handler defined separately, but using the Looper your custom thread.

May be.

 private void setUp() { mHandlerThread = new CustomThread("foo", Process.THREAD_PRIORITY_BACKGROUND); mHandlerThread.start(); // Create our handler; this will block until looper is initialised mHandler = new CustomHandler(mHandlerThread.getLooper()); // mHandler is now ready to use } private class CustomThread extends HandlerThread { public void run() { // ... } } private class CustomHandler extends Handler { CustomHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { // ... } } 
+4
source share

I would go with the classic wait / notify

 public class LooperThread extends Thread { private Handler mHandler; public void run() { Looper.prepare(); synchronized (this) { mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; notifyAll(); } Looper.loop(); } public synchronized Handler getHandler() { while (mHandler == null) { try { wait(); } catch (InterruptedException e) { //Ignore and try again. } } return mHandler; } } 

A handler returned from getHandler can be used many times without invoking a synchronized getHandler.

+11
source share

I just want to add that the verified answer is the best, but if you test it, it will not work, because you need to call super on the run method, since it is responsible for preparing the looper, so the code should be like:

 private void setUp() { mHandlerThread = new CustomThread("foo", Process.THREAD_PRIORITY_BACKGROUND); mHandlerThread.start(); // Create our handler; this will block until looper is initialised mHandler = new CustomHandler(mHandlerThread.getLooper()); // mHandler is now ready to use } private class CustomThread extends HandlerThread { public void run() { super.run() // <- VERY IMPORTANT OTHERWISE IT DOES NOT WORK // your code goes here } } private class CustomHandler extends Handler { CustomHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { // ... } 

}

+1
source share

All Articles