我使用AsynchTask来托管一个模拟器,该模拟器无限期运行,并在每个模拟步骤后发布结果。
将模拟循环限制在最大25Hz的背景下,并且只调用javascript函数的结果,它工作得"很好"。
除了在浏览器中更新webgl模型(看起来足够快),我还有两个东西需要从Android UI中更新:FPS指示器和带有代表某些值的TextViews面板。如果我们忘记FPS:
onProgressUpdate()函数已经被限制以25Hz的频率调用,以刷新模型。现在我使用另一个时间变量来限制,在这个方法内部,对另一个更新uitpanel textViews的方法的调用。它被限制在1Hz,比我实际想要的要少,但对于这种信息来说已经足够快了。这个方法是尽可能干净的,所有的视图之前都加载到一个变量中,我保持这个变量不会每次都加载它们。
效果是什么:看起来更新5个textview需要像一秒钟,所有的UI冻结,触摸移动非常非常滞后…
我降低了后台任务的优先级:
@Override
protected Boolean doInBackground(ModelSimulation... params) {
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
...
并在doInBackground方法的末尾使用Thread.yield()。这将使行为改善到我所解释的,如果没有这些命令,行为甚至更糟糕。
我的问题是:
-我可以减少更多的优先级,如果不是使用后台任务,我使用一个处理程序和我自己的线程?
-服务会改善UI的行为吗?
-为什么更新5 textViews需要这么长时间相比调用javascript函数,最终将不得不使用gpu来改变webgl模型?
- Android在任何意义上都不准备做动态应用程序吗?像测试传感器这样的应用程序是如何快速更新UI的?因为没有像textViews这样的标准组件?(像浏览器运行速度比textView快)
注意:即使减少刷新限制,每次更新HUD时也会产生延迟效果。事实上,我谈论5个textViews,但只有更新FPS指示器产生相同的暂停。看起来唯一需要切换到UI线程的事实已经消耗了这个时间。
编辑1:@Override
protected Boolean doInBackground(ModelSimulation... params) {
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
if(simulator.getSimulatorStatus().equals(SimulatorStatus.Connected)){
try {
while (true){
//TODO Propagate
long dur = (System.nanoTime()-time_tmp_data);
if(dur<Parameters.Simulator.min_hud_model_refreshing_interval_ns){
try {
long sleep_dur = (Parameters.Simulator.min_hud_model_refreshing_interval_ns-(System.nanoTime()-time_tmp_data))/1000000;
if(sleep_dur>0){
Thread.sleep(sleep_dur);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
time_tmp_data = System.nanoTime();
SpacecraftState sstate = propagate();
int progress = (int)((extrapDate.durationFrom(finalDate)/mission.sim_duration)*100);
if(sstate!=null){
SimResults results = new SimResults(sstate, progress);
simulator.getSimulationResults().updateSimulation(results.spacecraftState, results.sim_progress);
publishProgress();
}
if(isCancelled())
break;
Thread.yield();
}
} catch (OrekitException e) {
// TODO Auto-generated catch block
e.printStackTrace();
simulator.showMessage(simulator.getContext().getString(R.string.sim_orekit_prop_error)+": "+e.getMessage());
}
}
return true;
}
@Override
protected void onProgressUpdate(Void... values) {
//Update model by push
simulator.getSimulationResults().pushSimulationModel();
//Update GUI HUD
if(time_tmp_gui==0 || (System.nanoTime()-time_tmp_gui)>Parameters.Simulator.min_hud_panel_refreshing_interval_ns){
time_tmp_gui = System.nanoTime();
simulator.getSimulationResults().updateHUD();
}
}
如果我注释了simulator.getSimulationResults().updateHUD();或者直接调用方法的内容,它工作得"很好"。这个方法只改变了一些textview text:
public synchronized void updateHUD(){
//Log.d("Sim",System.currentTimeMillis()+": "+"pre update gui 1");
activity.runOnUiThread( new Runnable() {
@SuppressLint("ResourceAsColor")
public void run() {
if(view != null){
if(panel_time != null)
panel_time.setText(info.time.replace("T", " "));
if(panel_progress != null)
panel_progress.setProgress(info.progress);
if(panel_vel != null){
panel_vel.setText("Vel. "+String.format("%.2f", info.velocity)+" Km/s");
if(info.velocity>config.limit_velocity)
panel_vel.setTextColor(activity.getResources().getColor(R.color.panel_limit));
else
panel_vel.setTextColor(activity.getResources().getColor(R.color.panel_value));
}
if(panel_accel != null){
panel_accel.setText("Accel. "+String.format("%.2f", info.acceleration)+" Km/s2");
if(info.acceleration>config.limit_acceleration)
panel_accel.setTextColor(activity.getResources().getColor(R.color.panel_limit));
else
panel_accel.setTextColor(activity.getResources().getColor(R.color.panel_value));
}
if(panel_radium != null)
panel_radium.setText("Orbit radium: "+String.format("%.1f", info.orbit_radium)+" Km");
if(panel_mass != null)
panel_mass.setText("Mass: "+String.format("%.1f", info.mass)+" Kg");
if(panel_roll != null)
panel_roll.setText("Rol: "+String.format("%.1f", (180*info.roll/Math.PI))+"º");
if(panel_pitch != null)
panel_pitch.setText("Pitch: "+String.format("%.1f", (180*info.pitch/Math.PI))+"º");
if(panel_yaw != null)
panel_yaw.setText("Yaw: "+String.format("%.1f", (180*info.yaw/Math.PI))+"º");
}
}
});
//Log.d("Sim",System.currentTimeMillis()+": "+"post update gui 1");
}
编辑2:我实际上可以删除runonuthread,因为它已经在该线程,但效果是相同的,这不是问题。
编辑3:我试图注释方法updateHUD()的所有行,只留下这两个:if(panel_time != null)
panel_time.setText(info.time.replace("T", " "));
效果几乎是一样的,如果我触摸任何textView,动画会按照周期性冻结的步骤进行
编辑4:我注意到AsyncTask内部的进程花费的时间比可用的步骤时间长,所以它永远不会睡觉。我建立了一个10ms的安全保护时间,即使模拟步骤比可用时间长,也会休眠。所以每100毫秒中至少有10毫秒空闲时间。效果还是一样的。我以25Hz的浏览器和1Hz的单个textview文本更新。如果我禁用textview更新,webgl模型会平滑地动画。另一方面,如果我也启用textview update,每次文本更新时,会有几毫秒的时间浏览器动画和它对触摸的响应被阻塞。如果我提高任务优先级,这种影响会变得更糟。我试着设置一个500ms的大保护,但仍然出现冻结效果。我正在使用XWalkView,它可以阻止这个视图的交互当UI线程的行为?
我不明白为什么4核2 RAMgb设备需要比Linux或windows桌面PC更多的时间来计算相同的模拟。我有25Hz->40ms的可用时间,步骤花费近70ms。在PC上,我可以将模拟实时保持在25Hz。与其他操作系统相比,Android的后台运行的垃圾有那么多吗?
您的代码肯定还有其他问题。试着在这里发布你的AsyncTask。
你也可以尝试一些非常基本的东西,比如:
创建一个新的线程,每25Hz循环一次,并通过使用你的UI元素的post()方法或Activity的runInUiThread()方法更新你的UI。看看是否还有代码在UI线程中运行,可以做一些繁重的工作,可以在UI线程之外完成。
我几乎尝试了所有的东西,除了最合乎逻辑的东西,在没有调试器连接的情况下尝试应用程序。
比起PC的模拟速度更慢的原因是为了释放UI事件…这都是因为调试器从设备中占用了大量资源。所以,我想从这一点来看,我将不得不在没有调试器的情况下测试应用程序,这迫使我每次都重新启动手机以避免"等待调试器连接"。
感谢所有努力的人。
我可能错了,但我认为你的问题在synchronization
对simulator.getSimulationResults()
对象。我看不到simulator
类的实现和getSimulationResults()
返回的对象的实现,但是我猜想getSimulationResults()
每次都返回相同的对象。如果是,那么它看起来像这样:
- 在AsyncTaks中调用
simulator.getSimulationResults().updateSimulation(...)
。如果这个方法是同步的,那么这个调用将为AsyncTaks
线程锁定SimulationResults
对象。 -
updateSimulation(...)
返回,publishProgress()
被调用,但publishProgress()
只是在UI线程中调度onProgressUpdate(Void... values)
。 - AsyncTaks线程中的新迭代可以在UI线程获得控件并执行
onProgressUpdate(Void... values)
之前开始。所以,AsyncTaks线程进入第一步。UI线程获得控制并执行onProgressUpdate(Void... values)
和synchronized void updateHUD()
方法,但updateHUD()
不能执行,因为SimulationResults
对象被updateSimulation(...)
方法中的AsyncTaks线程锁定。UI线程将控制返回给操作系统。这可能会发生多次。
因此,onProgressUpdate(Void... values)
方法和UI线程中的所有事件只有在UI线程在正确的时刻获得控制时才能执行,而updateSimulation(...)
方法没有在AsyncTask线程中调用。
你可以通过替换public void update HUD()
上的public synchronized void update HUD()
来检查这个想法,并在TextView中随机写一些东西。
在任何情况下,在这种情况下使用AsyncTask都不是最好的主意。AsyncTask是在TheadPool中执行的,但是在Android系统中,这个池只能由一个线程组成。因此,所有的AsyncTask将在一个线程中一个接一个地执行。