Java 中处理延迟事件的最佳方法是什么
What is the best Way to Handle Delayed Events in Java
我正在尝试在 android 工作室制作一个简单的游戏,我需要能够延迟某些事件。
我首先进行了一些谷歌搜索,发现 Timer
和 TimerTask
似乎是一个不错的选择。但是,如果我的 activity 调用 onPause
没有 Timer.pause
,我必须取消整个事情。
所以,我决定继续创建自己的 class 来为我处理事件,并支持暂停。
我做了一个简单的 class (EventHandler
),它根据用户命令创建 "event(s)",并每 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;
}
});
我不太确定你想要完成什么,但听起来你可能对 ScheduledExecutorService 感兴趣。这允许您提交 Runnables
或 Callables
以在将来的特定时间播放。当活动结束时,他们还会自动将自己从 Queue
中移除。
这还没有解决?这听起来像是人们所做的那样取消并重新创建 TimerTasks 来模拟暂停:
Pausing/stopping and starting/resuming Java TimerTask continuously?
只有引用需要有效地最终化;可以从内部 class:
更改对象的状态
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 来安排事件,并在到期时从集合中拉出并由主循环执行。
由于这是 Android,您无法确定在 onPause 调用后您的应用程序是否完全从内存中删除。所以最好的方法是使用类似 ScheduledExecutorService 的东西。当 onPause 事件发生时取消预定的 Runnable。当调用 onResume 时,只需再次安排相同的 Runnable。
其他任何事情都可能导致 Android 的应用程序模型出现问题。可以从内存中删除处于暂停状态的应用程序。因此,您可能必须为自定义计时器实现保存状态。
我正在尝试在 android 工作室制作一个简单的游戏,我需要能够延迟某些事件。
我首先进行了一些谷歌搜索,发现 Timer
和 TimerTask
似乎是一个不错的选择。但是,如果我的 activity 调用 onPause
没有 Timer.pause
,我必须取消整个事情。
所以,我决定继续创建自己的 class 来为我处理事件,并支持暂停。
我做了一个简单的 class (EventHandler
),它根据用户命令创建 "event(s)",并每 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;
}
});
我不太确定你想要完成什么,但听起来你可能对 ScheduledExecutorService 感兴趣。这允许您提交 Runnables
或 Callables
以在将来的特定时间播放。当活动结束时,他们还会自动将自己从 Queue
中移除。
这还没有解决?这听起来像是人们所做的那样取消并重新创建 TimerTasks 来模拟暂停:
Pausing/stopping and starting/resuming Java TimerTask continuously?
只有引用需要有效地最终化;可以从内部 class:
更改对象的状态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 来安排事件,并在到期时从集合中拉出并由主循环执行。
由于这是 Android,您无法确定在 onPause 调用后您的应用程序是否完全从内存中删除。所以最好的方法是使用类似 ScheduledExecutorService 的东西。当 onPause 事件发生时取消预定的 Runnable。当调用 onResume 时,只需再次安排相同的 Runnable。
其他任何事情都可能导致 Android 的应用程序模型出现问题。可以从内存中删除处于暂停状态的应用程序。因此,您可能必须为自定义计时器实现保存状态。