2016-11-14 2 views
1

Я пытаюсь сделать простую игру в студии Android, и мне нужно уметь откладывать определенные события.Каков наилучший способ обработки отложенных событий в Java

Я начал с выполнения поиска в Интернете и обнаружил, что Timer с TimerTask представляется довольно хорошим вариантом. Однако, если моя деятельность вызывает , нет Timer.pause, я должен отменить все это.

Итак, я решил продолжить свой собственный класс, который обрабатывал бы события для меня и поддерживал бы паузу.

Я сделал простой класс (EventHandler), который создает «событие (ы)» по команде пользователей, а также циклы через ArrayList событий каждые 10 миллисекунд, чтобы увидеть, если System.currentTimeMillis> = eventFinishedTime. Если событие завершено, он вызывает метод inteface, и событие удаляется из ArrayList.

Но теперь я столкнулся с новой проблемой.

EventHandler вызывает метод интерфейса (onFinished), когда событие завершено, поэтому я не могу использовать переменную, которая не объявлена ​​окончательной в onFinished. Единственный способ обойти это, что я могу найти, - это сделать новый метод для каждого случая, когда я хочу задержать событие, которое кажется плохим.

Так что мой вопрос: Каков наилучший способ сделать это или как вы это сделаете?

Не стесняйтесь спрашивать, хотите ли вы увидеть мой код, просто укажите, какая часть :) Также не стесняйтесь спрашивать о дополнительной информации ... Я подвел итог довольно много, и я более чем готов попробовать объясните далее, с примерами.

Спасибо!

Вот EventHandler.class (я не включил импорт ... Прокрутка к нижней части, чтобы увидеть, где вызывается метод интерфейса):

public class EventHandler extends Thread { 
    //Constants 
    final String TAG = "EventHandler"; 
    final long WAIT_TIME = 10; 


    public ArrayList<Event> events = new ArrayList<>(); //Every WAIT_TIME the run() funtion cycles through this list and checks if any events are complete 
    public boolean runChecks = true; //If true, the run() function goes (It's only false while the DoDayActivity tells it to pause 
    public long pauseStartTime; //This value tags the System.currentTimeMillis() @pauseCheck 
    public long totalPausedTime = 0; //This value contains how long the EventHandler was paused 
    Activity activity; 

    public EventHandler(Activity activity) { 
     this.activity = activity; 
    } 

    public void run() { 
     //checking the listeners 
     while (true) { 
      if (runChecks) {//Making sure the timer isn't paused 
       checkListeners(); 
      } 
      try { 
       Thread.sleep(WAIT_TIME); //Yes I am using Thread.sleep(), kill me 
      } catch (Exception ignore) { 
      } 
     } 

    } 

    public interface OnEventListener { 
     void onFinished(); 
    } 


    public void createEvent(String name, long milliseconds, OnEventListener eventListener) {//This is how an event is created, see the private Event class below 
     new Event(this, name, milliseconds, eventListener); 
    } 

    public void checkListeners() { 
     for (Event event : events) { 
      event.amIFinished();//A method that checks if the event has reached its end time 
     } 
    } 

    public void pauseCheck() { //"Pauses" the timer (Probably not the best way, but it is simple and does what I need it to 
     runChecks = false; 
     pauseStartTime = System.currentTimeMillis(); 
    } 

    public void resumeCheck() {//Resumes the timer by adding the amount of time the EventHandler was paused for to the end if each event 
     try { 
      if ((pauseStartTime > 99999999)) {//For some reason, when an activity is created, onResume is called, so I added this in there to prevent glicthes 
       totalPausedTime = System.currentTimeMillis() - pauseStartTime; 
       Log.d(TAG, "Resuming, adding " + String.valueOf(totalPausedTime) + " milliseconds to each event"); 
       for (Event e : events) { 
        e.end += totalPausedTime; 
       } 
      } 
     } catch (Exception e) { 
      Log.w(TAG, "During resume, EventHandler tried to add time to each of the events, but couldn't!"); 
      e.printStackTrace(); 
     } 

     runChecks = true; 

    } 


    private class Event { //Here is the class for the event 
     public long start; 
     public long end; 
     OnEventListener listener; 
     EventHandler parent; 
     String name; 

     public Event(EventHandler parent, String name, long milliseconds, OnEventListener listener) { 
      start = System.currentTimeMillis(); 
      end = start + milliseconds; 
      this.listener = listener; 
      this.parent = parent; 
      this.name = name; 

      //Adding itself to the ArrayList 
      parent.events.add(this); 
     } 

     public void amIFinished() {//Method that checks if the event is completed 
      if (System.currentTimeMillis() >= end) {//Removes itself from the arraylist and calls onFinished 
       Log.d(TAG, "Completed " + name); 
       parent.events.remove(this); 
       activity.runOnUiThread(new Runnable() { 
        @Override 
        public void run() { 
         listener.onFinished(); //This is where the interface method is called! 
        } 
       }); 
      } 
     } 
    } 
} 

Вот где я пытаюсь использовать его (Это просто пример использования INT х):

int x = 0; 

eventHandler = new EventHandler(this); 
eventHandler.start(); 
eventHandler.createEvent("Change X Value", 800, new EventHandler.OnEventListener() { 
    @Override 
    public void onFinished() { 
     //x is not declared final so it will not work 
     x = 5; 

    } 
}); 
+0

Обновлено сообщение – Pythogen

+0

Вы не должны использовать 'ArrayList' в программе cuncurrent. См. Альтернативные классы 'CopyOnWriteArrayLis',' ConcurrentLinkedQueue', ... –

ответ

1

Это только ссылка, которая должна быть эффективно окончательным; это нормально, чтобы изменить состояние объекта из внутреннего класса:

AtomicInteger x = new AtomicInteger(0); 

eventHandler = new EventHandler(this); 
eventHandler.start(); 
eventHandler.createEvent("Change X Value", 800, new EventHandler.OnEventListener() { 
    @Override 
    public void onFinished() { 
     // x is effectively final so we can reference it 
     x.set(5); 
    } 
}); 

Или ... с помощью лямбда

eventHandler.createEvent("Change X Value", 800,() -> x.set(5)); 

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

События могут быть запланированы путем добавления их в TreeSet, который сортирует по времени игры и вытаскивает из набора и выполняется по основному циклу, когда они станут обязательными.

+0

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

+0

@Pythogen: Я нашел это ... http://gameprogrammingpatterns.com/game-loop.html – teppic

0

Я не совсем уверен, что вы пытаетесь достичь, но это звучит, как вы можете быть заинтересованы в ScheduledExecutorService. Это позволяет вам отправить Runnables или Callables для воспроизведения в определенное время в будущем. Они также автоматически удаляются из Queue, когда событие завершено.

0

Поскольку это Android, вы не можете быть уверены, что после вызова onPause ваше приложение не будет полностью удалено из памяти. Поэтому лучший способ - использовать что-то вроде ScheduledExecutorService. Отменить запланированное Runnable, когда происходит событие onPause. Когда вызывается onResume, просто планируйте одно и то же Runnable снова.

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

+0

Хорошо, но, к счастью, мне нужна только функция паузы, если пользователь быстро переключает приложения, или если я сделаю паузу кнопка и т. д. Мне не нужно возобновлять работу с точного времени, когда приложение перерабатывается, поскольку в игре будут сохранены контрольные точки. – Pythogen