在调用之前,如何确保另一个Thread的Handler不为null?

时间:2010-02-02 20:32:22

标签: java android multithreading null

我的程序在前几天尝试使用在另一个线程上创建的Handler向该线程发送消息时抛出了NullPointerException。尽管调用线程已经在另一个线程上调用了start,但是其他线程创建的Handler尚未创建,或者尚未对调用线程可见。这种情况很少发生。几乎每次测试都没有得到例外。

我想知道什么是最好的方法来避免这个问题,确保最小的复杂性和性能损失。该程序是一款游戏,性能非常敏感,特别是一旦运行。因此,我尝试避免在设置后使用同步,例如,并且宁愿在任何时候避免旋转变量。

背景:
在Android中,Handler类可用于“将行为排入队列,而不是在您自己的线程上执行”。文档:
http://developer.android.com/intl/de/reference/android/os/Handler.html

必须在将使用它的线程上创建Handler。因此,在创建该线程的线程运行的线程的构造函数中创建它不是一个选项。

当Handler用于UI线程以外的线程时,还必须使用Looper类:
http://developer.android.com/intl/de/reference/android/os/Looper.html

文档给出了为此目的使用这两个类的示例:

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

我非常丑陋的解决方法目前看起来像这样:

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

}

创建线程中的代码如下所示:

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

有没有更好的解决方案?感谢。

3 个答案:

答案 0 :(得分:12)

我会选择经典的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;
    }
}

然后可以多次使用从getHandler返回的处理程序,而无需调用同步的getHandler。

答案 1 :(得分:5)

准备Looper可以阻止一段时间,所以我想你会遇到prepare()需要一点时间才能完成的情况,因此mHandler仍未定义。

您可以Thread延长HandlerThread,但即使这样,您仍需等待确保Looper已初始化。也许这样的事情可能有用,你可以单独定义Handler,但是使用自定义线程的Looper

可能。

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) {
        // ...
    }
}

答案 2 :(得分:1)

我只想补充一点,检查答案是最好的答案,但如果你测试它就不会起作用因为你需要在运行方法上调用super,因为它负责准备looper所以代码应该是像这样:

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) {
    // ...
 }

}