我的游戏有一个统计队列,每场比赛后当前的游戏统计数据进入队列。 每当主菜单开始时我想将所有游戏统计数据上传到服务器,这需要1-3秒,我想在另一个线程中执行此操作。
我的代码
@Override
public void show() {
Global.key = Global.getKey();
// System.out.println(Stats.getJSONObject(Global.key));
Gdx.app.postRunnable(new Runnable() {
@Override
public void run() {
Stats.TryUploadGame1();
System.out.println("DONE");
}
});
.....
}
但这也冻结了我的游戏。
我该怎么办?
答案 0 :(得分:4)
您当前的代码正在发布Runnable
实例,该实例将在下一帧之前由渲染线程执行。通常使用Gdx.app.postRunnable
API,因此后台线程可以要求在渲染线程上发生某些事情。您希望发布Runnable
以执行除渲染线程以外的任何位置。
只要你的统计代码根本不与OpenGL上下文交互(因为Android OpenGL API假设只有一个线程与它们交互),你可以在新的后台线程上发布你的Runnable
: / p>
new Thread(new Runnable() { ... }).start();
这应取消阻止你的渲染。 (当然,如果你的后台线程使用了大量的CPU,它仍然会干扰渲染线程,但如果它主要阻塞IO或主机有备用内核,它就不应该干扰。)
这可以通过多种方式进行改进(使用ThreadPool,或使用Android感知后台任务支持),但如果您的统计信息更新速度相对较快且线程创建频繁,则应该可以正常工作。
答案 1 :(得分:3)
好的,在其他线程中做一些事情,你需要处理 OpenGL上下文。在一个不同的线程内,你不能做任何渲染东西的事情。你被迫以任何方式将这种东西推入渲染中。你需要synchronize
可以从libgdx的常规渲染线程中调用的所有东西。例如,你想从一个不同线程的舞台调用.act(float delta)
,你被迫将舞台indo放在synchronized
块中。
可运行的帖子不是一个帖子。它是一个可运行的,在下一个rendercall的开头执行。因此它将停止游戏,直到它完成但它在OpenGl上下文线程内。 (这就是你的游戏停止的原因)
以下是如何在libgdx中使用线程的示例。我在游戏中使用它。它运行210帧,每秒210次更新调用。您可以将其更改为尽可能快或只需60fps即可:
public class GameLogicThread extends Thread {
private GameScreen m_screen;
private boolean m_runing;
private long m_timeBegin;
private long m_timeDiff;
private long m_sleepTime;
private final static float FRAMERATE = 210f;
public GameLogicThread(GameScreen screen) { //pass the game screen to it.
m_screen = screen;
setName("GameLogic");
}
@Override
public void run() {
m_runing = true;
Logger.log("Started");
while (m_runing) {
m_timeBegin = TimeUtils.millis();
// act of the camera
synchronized (m_screen.figureStage) { //stage with figures
// now figures
if (m_screen.m_status == GameStatus.GAME) {
m_screen.figureStage.act(1f / GameLogicThread.FRAMERATE);
}
}
m_timeDiff = TimeUtils.millis() - m_timeBegin;
m_sleepTime = (long) (1f / GameLogicThread.FRAMERATE * 1000f - m_timeDiff);
if (m_sleepTime > 0) {
try {
Thread.sleep(m_sleepTime);
}
catch (InterruptedException e) {
Logger.error("Couldn't sleep " + e.getStackTrace());
}
} else {
Logger.error("we are to slow! " + m_sleepTime); //meight create it dynamic so if you are to slow decrease the framerate till you are not to slow anymore
}
}
}
/**
* Stops the thread save<br>
*/
public void stopThread() {
m_runing = false;
boolean retry = true;
while (retry) {
try {
this.join();
retry = false;
}
catch (Exception e) {
Logger.error(e.getMessage());
}
}
}
}
这确实更新了我的所有数据。为了不使渲染线程出现任何问题,使得图形台同步。 (关键部分的种类)
不要忘记每次停止时都需要创建一个新线程。所以例如在节目内你需要这个:
@Override
public void show() {
super.show();
m_logic = new GameLogicThread(this); //create a new one inside of the GameScreen
m_logic.start(); //start the thread
}
也不要忘记在暂停站内等待它。等等。
@Override
public void dispose() {
m_logic.stopThread();
}
答案 2 :(得分:1)
根据wiki
要从另一个线程将数据传递到呈现线程,我们建议使用Application.postRunnable()。这将在调用ApplicationListener.render()之前在下一帧的渲染线程中运行Runnable中的代码。
因此,调用该方法只是创建一个新的线程,以便在渲染线程上运行。
你可能想在创建线程时使用标准的java实践,除非在libgdx中因为android而不赞成,我不确定。