Как я удостоверяюсь, что Обработчик другого Потока не является пустым прежде, чем назвать его?

Моя программа бросила NullPointerException на днях, когда она пыталась использовать Обработчик, созданный на другом потоке, чтобы отправить тому потоку сообщение. Обработчик, созданный другим потоком, еще не был создан или еще не видимый к вызывающему потоку, несмотря на вызывающий поток, уже звонивший, запускаются на другом потоке. Это только происходит очень редко. Почти каждый тестовый прогон не получает исключение.

Я задавался вопросом, что лучший способ состоит в том, чтобы избежать этой проблемы наверняка с минимальной сложностью и потерей производительности. Программа является игрой и очень чувствительной производительностью, особенно после того как она работает. Поэтому я стараюсь не использовать синхронизацию после установки, например, и предпочел бы не вращаться на переменной в любое время.

Фон:
В Android класс Обработчика может использоваться для "постановки в очередь действия, которое будет выполнено на другом потоке, чем собственное". Документация здесь:
http://developer.android.com/intl/de/reference/android/os/Handler.html

Обработчик должен быть создан на потоке, где он будет использоваться. Так создание его в конструкторе потока, который выполняется потоком, создающим тот поток, не является опцией.

Когда Обработчик для потока кроме потока UI, класс Выполняющего мертвую петлю летчика должен также использоваться:
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 setupComplete = new ArrayBlockingQueue(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);

Есть ли какие-либо лучшие решения?Спасибо.

7
задан Lance Nanek 2 February 2010 в 20:32
поделиться

2 ответа

Подготовка 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) {
        // ...
    }
}
5
ответ дан 6 December 2019 в 10:50
поделиться

Я бы пошел с 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;
    }
}

обработчик, возвращенный из GetHandler, можно использовать много раз, не вызывая синхронизированного GetHandler.

12
ответ дан 6 December 2019 в 10:50
поделиться
Другие вопросы по тегам:

Похожие вопросы: