Несогласованный обмен сообщениями между обработчиком и исполняемым

Я пытаюсь обновить пользовательский интерфейс из фонового потока через обработчик. Runnable периодически отправляет (минимум раз в две секунды, максимум 16 раз в секунду) новые данные обработчику для обновления. В моем рабочем классе у меня есть флаг для запуска/остановки цикла - в основном классе есть методы, прикрепленные к кнопкам для этой задачи.

У меня есть несколько проблем с этим:

  1. Первое полученное сообщение всегда 0
  2. После нескольких циклов сообщение, полученное обработчиком, всегда равно 0
  3. Время от времени (извините, я знаю, плохая точность) нажатие кнопки «Стоп» вызывает исключение IllegalStateException. Вот код (упрощенный):

Основная деятельность:

public class MainActivity extends Activity
{

    OtherClass otherObbject;
    Thread otherClassThread;
    Handler handler;
    TextView txtBeat;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        txtBeat = (TextView) findViewById(R.id.txtBeat);

        handler = new Handler()
        {
            int beat;

            @Override
            public void handleMessage(Message msg)
            {
                msg = this.obtainMessage();
                beat = msg.getData().getInt("beat");
                txtBeat.setText(String.valueOf(beat));
            }
        };
    }

    public void onPlay(View v)
    {
        otherObbject = new OtherClass(handler);
        otherClassThread = new Thread(otherObbject);
        otherObbject.start();
        otherClassThread.start();
    }

    public void onStop(View v)
    {
        otherObbject.stop();
    }

}

Запускаемый класс:

public class OtherClass implements Runnable
{
    private Boolean isPlaying;
    private Handler mHandler;
    private int beatMaximum;
    private long waitTime;
    private long maxWait;

    public OtherClass(Handler handler)
    {
        isPlaying = false;
        mHandler = handler;
        beatMaximum = 4;
        waitTime = 30;
        maxWait = 500;
    }

    @Override
    public void run()
    {
        int counter = 0;
        int beat = 1;
        Bundle bundle = new Bundle();
        Message msg = new Message();

        while (isPlaying)
        {
            if(counter == 0)
            {
                //Do some stuff

                System.out.println(beat);
                bundle.putInt("beat", beat);
                msg.setData(bundle);
                mHandler.sendMessage(msg);

                if (beat == beatMaximum)
                    beat = 1;
                else
                    beat++;
            }
            else
                waitThread();

            if ((counter += waitTime) >= maxWait)
                counter = 0;
        }
    }

    private void waitThread()
    {

        try
        {
            Thread.currentThread().sleep(waitTime);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }

    public void start()
    {
        isPlaying = true;
    }

    public void stop()
    {
        isPlaying = false;
    }
}

И исключение:

FATAL EXCEPTION: main
java.lang.IllegalStateException: The specified message queue synchronization  barrier token has not been posted or has already been removed.
        at android.os.MessageQueue.removeSyncBarrier(MessageQueue.java:266)
        at android.os.Looper.removeSyncBarrier(Looper.java:242)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:990)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4212)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:725)
        at android.view.Choreographer.doCallbacks(Choreographer.java:555)
        at android.view.Choreographer.doFrame(Choreographer.java:525)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:711)
        at android.os.Handler.handleCallback(Handler.java:615)
        at android.os.Handler.dispatchMessage(Handler.java:92)
        at android.os.Looper.loop(Looper.java:137)
        at android.app.ActivityThread.main(ActivityThread.java:4745)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:511)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
        at dalvik.system.NativeStart.main(Native Method)

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

В идеале AsyncTasks следует использовать для коротких операций (максимум несколько секунд).

Спасибо за ваше время.


person Dani    schedule 27.11.2014    source источник
comment
в handleMessage() удалить msg =getMessage()   -  person pskink    schedule 27.11.2014
comment
Готово, но я получаю только первое сообщение. Сразу после этого приложение вылетает с опубликованным исключением:/   -  person Dani    schedule 27.11.2014
comment
могу я спросить, для чего вам нужен этот фоновый поток, поскольку он не выполняет никакой тяжелой работы?   -  person pskink    schedule 27.11.2014
comment
Конечно, я не публиковал нерелевантный код. Это метроном; фоновый поток постоянно передает объект AudioTrack, который издает звуковые сигналы. Я хочу обновить пользовательский интерфейс в соответствии с темпом.   -  person Dani    schedule 27.11.2014
comment
вам не нужен поток для этого, просто используйте обработчик или CountDownTimer   -  person pskink    schedule 27.11.2014
comment
Таймеры не подходят для этого, пока мне нужна стабильная синхронизация с несколькими миллисекундами между звуковыми сигналами, в противном случае они совершенно бесполезны. После долгих исследований и тестов единственным способом, которым я мог обеспечить точное время, было использование AudioTrack в потоковом режиме, непрерывно подаваемом гудками и тишиной. В любом случае, я проверю, как это сделать, просто используя обработчик.   -  person Dani    schedule 27.11.2014
comment
я не имел в виду Timers/TimerTasks, я имел в виду Handler или CountDownTimer   -  person pskink    schedule 27.11.2014
comment
Хорошо, я только что попробовал с CountDownTimer, и, как и ожидалось, точность - это боль. Я предполагаю такое же поведение с обработчиком, поэтому давайте вернемся к моему первоначальному вопросу о том, как решить мои проблемы с обменом сообщениями.   -  person Dani    schedule 27.11.2014
comment
хорошо, но вы пробовали Handler.send[Empty]Message[Delayed/AtTime]? я думаю, что версия AtTime была бы полезна   -  person pskink    schedule 27.11.2014
comment
Нет, я этого не делал. Часы для этого просто не годятся, так как их точность зависит от загрузки процессора, и опять же мне нужна очень высокая точность. Поверьте, я провел с ним много часов.   -  person Dani    schedule 28.11.2014
comment
в какой теме вы хотите использовать свой AudioTrack? в фоновом потоке или в основном потоке пользовательского интерфейса? если фон (и я считаю, что это ваш выбор) вам вообще не нужно спать, просто запишите данные в AudioTrack и вызовите Handler.obtainMessage(what).sendToTarget(); чтобы уведомить поток пользовательского интерфейса   -  person pskink    schedule 28.11.2014
comment
Да, это на заднем плане. Если вы посмотрите на мой код, я ничего не сплю. Я загружаю AudioTrack, где вы можете увидеть некоторые вещи, затем я помещаю данные в сообщение и отправляю его обработчику. Я только что попробовал, как вы говорите (sendToTarget()), и я получаю то же поведение, что и с sendMessage(), то есть первое всегда равно нулю, второе равно 2, как и ожидалось, и через некоторое время весь доход сообщения равны 0, пока отправляется правильный номер.   -  person Dani    schedule 28.11.2014
comment
Я также вижу метод waitThread, вызываемый в ветке else, можете ли вы опубликовать свой текущий код?   -  person pskink    schedule 28.11.2014
comment
Да, прости. Это ожидание предназначено только для целей моделирования. В моем коде я добавляю тишину в AudioTrack в этот момент.   -  person Dani    schedule 28.11.2014
comment
Поэтому опубликуйте свой код с тем, что я предложил: Handler.obtainMessage(what).sendToTarget();   -  person pskink    schedule 28.11.2014
comment
Таймеры не подходят для этого, пока мне нужно надежное время с несколькими миллисекундами между звуковыми сигналами, в противном случае это совершенно бесполезно - тогда вам нужно будет реализовать все это так, как вы бы сделали игру. Вы не получите надежного тайминга, используя Handler. Возьмите книгу по разработке игр для Android, посмотрите, как они реализуют 2D-игру с помощью Canvas, и адаптируйте эти методы к своему приложению.   -  person CommonsWare    schedule 28.11.2014
comment
Хорошо, я посмотрю на методы разработки игр   -  person Dani    schedule 02.12.2014