如何确保另一个线程的处理程序在调用之前不为null?


问题内容

前几天,当我的程序尝试使用在另一个线程上创建的处理程序向该线程发送消息时,抛出NullPointerException。尽管调用线程已经在另一个线程上调用了start,但是尚未创建另一个线程创建的处理程序,或者该调用程序尚未看到该线程。这种情况很少发生。几乎每个测试运行都不会获得异常。

我想知道最好的方法是以最小的复杂性和性能损失来确保避免此问题。该程序是一款游戏,对性能非常敏感,尤其是在运行后。因此,例如,我尝试避免在设置后使用同步,并且希望避免随时在变量上旋转。

背景:
在Android中,Handler类可用于“使要在与您自己的线程不同的线程上执行的动作排队”。此处的文档: http
//developer.android.com/intl/de/reference/android/os/Handler.html

必须在将使用处理程序的线程上创建处理程序。因此,在创建该线程的线程运行的线程的构造函数中创建它不是一种选择。

当处理程序用于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);

有更好的解决方案吗?谢谢。


问题答案:

准备一个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) {
        // ...
    }
}