Как сообщить родительскому потоку, что слушатель был вызван? - PullRequest
1 голос
/ 21 марта 2012

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

Метод myMethod () запрашивает, чтобы SoundPool загрузил звук и уведомил слушателя о завершении вызова. Я хотел бы, чтобы myMethod () дождался вызова слушателя, прежде чем вернуться. Код, который я придумал:

void myMethod() {
    SoundPool soundPool = ...;
    final Lock object = new Object();

    soundPool.setOnLoadCompleteListener(new OnLoadCompleteListener(){
        @Override
        public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
            synchronized(lock) {
                lock.notify();
            }
        }});
    synchronized(lock) {
        int soundId = MediaUtil.loadSoundPool(soundPool, ...); // returns immediately
        lock.wait();
    }
}

(я упустил try / catch для простоты, хотя это и есть в реальном коде.) Обычно у меня также есть флаг при обмене данными между двумя потоками, но я не могу использовать локальную переменную для этой цели, так как он должен быть последним, чтобы быть доступным из анонимного класса, а переменная экземпляра выглядит как нарушение абстракции (и должна быть изменчивой).

Безопасен ли вышеуказанный код и / или есть ли лучший способ решить проблему?

Ответы [ 2 ]

2 голосов
/ 21 марта 2012

Множество вариантов выполнения того, что вам нужно. Вот один из способов сделать это с помощью ожидания / уведомления. IsNotific здесь для предотвращения зависания в случае, когда onLoadComplete () вызывается тем же потоком, который вызывает setListener (). Класс MonitorXX предназначен для упрощения отладки, когда вы смотрите на дампы потоков из больших приложений. При необходимости добавьте проверку ошибок / исключений / времени.

static class Monitor42{boolean isNotified=false;} 
static Monitor42 monitor = new Monitor42(); 
void myMethod() { 
    synchronized(monitor) {
        monitor.isNotified=false;
        xxx.setListener(new Listener(){ 
        public void onLoadComplete() { 
            synchronized(monitor) {
                monitor.isNotified=true;
                monitor.notify(); 
            } 
        }}); 
        if (!monitor.isNotified) monitor.wait(someTimeValueMS);
        monitor.isNotified=false;
    } 
} 
1 голос
/ 21 марта 2012

В основном я пытаюсь заставить асинхронный вызов быть синхронным.

Канонический способ сделать это именно то, что вы делаете: вызвать уведомление в концеасинхронная работа в фоновом потоке и ожидание уведомления в основном потоке.

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

Простой способ обойтиэто ограничение заключается в использовании final Boolean[] flag = new Boolean[1]; flag[0] = false.

...