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

Lan*_*nek 7 java null multithreading android

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

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

背景:
在Android中,Handler类可用于"在不同于您自己的线程上执行操作".这里的文档:http:
//developer.android.com/intl/de/reference/android/os/Handler.html

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

当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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

}
Run Code Online (Sandbox Code Playgroud)

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

    LooperThread otherThread = new LooperThread();
    otherThread.start();        
    otherThread.waitForSetupComplete();
    otherThread.mHandler.sendEmptyMessage(0);
Run Code Online (Sandbox Code Playgroud)

还有更好的解决方案吗?谢谢.

And*_*min 12

我会选择经典的等待/通知

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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


Chr*_*Orr 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) {
        // ...
    }
}
Run Code Online (Sandbox Code Playgroud)