【问题标题】:What is the best Way to Handle Delayed Events in Java在 Java 中处理延迟事件的最佳方法是什么
【发布时间】:2017-03-27 15:06:37
【问题描述】:

我正在尝试在 android studio 中制作一个简单的游戏,我需要能够延迟某些事件。

我开始使用谷歌搜索,发现TimerTimerTask 似乎是一个不错的选择。但是,如果我的活动调用onPause 没有Timer.pause,我必须取消整个活动。

因此,我决定继续创建自己的类来为我处理事件并支持暂停。

我创建了一个简单的类 (EventHandler),它根据用户命令创建“事件”,并每 10 毫秒循环一次事件的 ArrayList 以查看 System.currentTimeMillis >= eventFinishedTime。如果事件完成,则调用一个接口方法,并将事件从 ArrayList 中移除。

但现在我遇到了一个新问题。

EventHandler 在事件结束时调用接口方法 (onFinished),所以我不能使用在 onFinished 中未声明为 final 的变量。我能找到的唯一解决方法是在每次我想延迟事件时创建一个新方法,这似乎是一种不好的做法。

所以我的问题是,最好的方法是什么,或者你会怎么做?

如果您想查看我的代码,请随时询问,只需指定哪一部分即可:) 也请随时询问更多信息...我总结了很多,并且非常愿意尝试通过示例进一步解释。

谢谢!

这里是 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 x 的示例):

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;

    }
});

【问题讨论】:

  • 你不应该在当前程序中使用ArrayList。查看替代课程CopyOnWriteArrayLisConcurrentLinkedQueue、...

标签: java android timer timertask


【解决方案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);
    }
}); 

或者...使用 lambda

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

如果我这样做,我会抽象游戏时间线。在您的主循环中增加一个 tick 计数器,并在事件到期时处理事件,而不是实时处理。

然后可以通过将事件添加到按游戏时间排序的 TreeSet 来安排事件,并在到期时从集合中拉出并由主循环执行。

【讨论】:

  • 关于抽象游戏时间线的非常有趣的想法,但是当你说可以拉出并执行预定的事件时......我不确定如何告诉事件执行什么而不遇到我的当前的问题。您能否举个例子或参考一些有关如何解决此问题的链接?这似乎是一条不错的路线。
【解决方案2】:

我不确定您要完成什么,但听起来您可能对ScheduledExecutorService 感兴趣。这允许您提交RunnablesCallables 以在未来的特定时间播放。活动结束后,他们还会自动将自己从Queue 中删除。

【讨论】:

    【解决方案3】:

    这没有解决它?听起来取消和重新创建 TimerTasks 来模拟暂停是人们所做的:

    Pausing/stopping and starting/resuming Java TimerTask continuously?

    【讨论】:

      【解决方案4】:

      由于这是 Android,因此您无法确定在 onPause 调用后您的应用程序没有完全从内存中删除。所以最好的方法是使用像 ScheduledExecutorService 这样的东西。当 onPause 事件发生时取消计划的 Runnable。调用 onResume 时,只需再次调度相同的 Runnable。

      其他任何事情都可能导致 Android 应用模型出现问题。处于暂停状态的应用程序可以从内存中删除。因此,您可能必须为自定义计时器实现保存状态。

      【讨论】:

      • 好点,但幸运的是,如果用户真正快速切换应用程序,或者如果我制作暂停按钮等,我只需要暂停功能。我不需要从应用程序的确切时间恢复被回收,因为游戏会有检查点保存它。
      猜你喜欢
      • 2020-06-28
      • 2015-04-24
      • 1970-01-01
      • 1970-01-01
      • 2013-10-23
      • 2019-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多