在我的UI中,我有一个需要实时更新的ImageView (箭头图像)。
箭头有两种可能的运动:
我已经有了这两种方法来正常工作。
唯一的问题是我的UI非常慢,有时会卡住。同时,我的手机总是变得非常热,而应用程序正在运行。Logcat有时也告诉我
跳过*帧。应用程序可能在其主线程上做了太多的工作。
我被告知使用AsyncTask,以避免强调我的UI。因此,我在AsyncTask中进行箭头更新工作。然而,问题仍然存在。我的UI仍然很慢。
我想我的AsyncTask实现应该有问题。我把它们贴在这里如下:
public class ArrowheadUpdater extends AsyncTask<Float, Integer, Float> { // Float: azimuth, Integer: state
private ImageView arrowheadToRotate;
private float rotationAngle; // also in radians
// constructor
public ArrowheadUpdater(ImageView _arrowheadToRotate) {
arrowheadToRotate = _arrowheadToRotate;
rotationAngle = -1;
}
protected void onPreExecute(Float _azimuth) {
super.onPreExecute();
}
@Override
// executed first to get the angle to rotate
protected Float doInBackground(Float... arg0) {
rotationAngle = (float) (Constant.MAP_ORIENTATION_OFFSET + arg0[0]);
return rotationAngle;
}
protected void onProgressUpdated(Integer... progress) {
super.onProgressUpdate(progress);
}
protected void onPostExecute(Float result) {
super.onPostExecute(result);
\\ rotation happens here
rotateImageView(ShowPathActivity.this, arrowheadToRotate, R.drawable.marker, result);
\\ moving happens here
movaImageView(arrowhead, MapView.historyXSeries, MapView.historyYSeries);
},我这样称呼AsyncTask:
// called when sensor values change
public void onSensorChanged(SensorEvent event) { // is roughly called 350 times in 1s
//...
if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
compassChangedTimes++;
magneticField[0] = event.values[0];
magneticField[1] = event.values[1];
magneticField[2] = event.values[2];
SensorManager.getRotationMatrix(RotationM, I, gravity, magneticField);
SensorManager.getOrientation(RotationM, direction);
if (compassChangedTimes % 50 == 0) {
// HERE!!!!!!!!!!
new ArrowheadUpdater(arrowhead).execute(direction[0]);
}
}
if (startFlag)
dataCollector.saveDataShowPath(acceleration, magneticField, startTime, currentTime);
}我应该把两个箭头更新方法放在doInBackground()而不是onPostExecute()中吗?
但是doInBackground()中的行可以更新UI吗?我不确定。
我的AsyncTask**?** 有什么问题吗?
其他猜测或评论是非常欢迎的!
更多线索:
我只是注意到,当我进入这个活动时,UI非常慢,并且经常被卡住。但过了一段时间,比如说10秒,它就变得可以接受地平滑了。
发布于 2013-07-17 08:13:25
AsyncTasks用于在后台执行繁重/冗长的操作,并将结果推送到UI上。
在您的示例中,您的AsyncTask没有执行繁重的操作,因此您可能可以放弃它。
此外,在当前代码中,UI更新的频率没有限制。您可以使用Handler实现这样的限制。
public static final int ARROW_MESSAGES = 0;
private Float angle;
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
// Discard other messages
removeMessages(ARROW_MESSAGES);
// UI update
rotateImageView(ShowPathActivity.this, arrowheadToRotate, R.drawable.marker, angle);
movaImageView(arrowhead, MapView.historyXSeries, MapView.historyYSeries);
}
}
// (...)
if (compassChangedTimes % 50 == 0) {
float res = (float) (Constant.MAP_ORIENTATION_OFFSET + direction[0]);
if (Math.abs(res - angle) > 1) {
angle = res;
handler.sendEmptyMessage(ARROW_MESSAGES);
}
}原则是向处理程序发送消息,但处理程序将只选择第一条消息并丢弃其余消息(在发布之前还可以测试处理程序以确定是否已经存在任何消息,我不确定哪一条更有效)。
这样,UI将使用最新的角度值进行更新,而不会尝试显示所有中间阶段。
另外,diff测试避免了不必要的更新(我假设1度足够精确,您甚至可能希望在那里设置一个更大的值)。
发布于 2013-07-17 07:55:16
你的问题是,你每秒钟接到350个对这个函数的调用,而忽略“垃圾”数据却做得不好。
首先,将尽可能多的逻辑移动到您的compassChangedTimes条件中(同时,您应该重置计数以避免溢出)。更好的是,决定是否根据传感器更改阈值而不是任意数量的样本更新UI (如果传感器报告一个常量值,并且偶尔调用onChangeEvent()函数,会发生什么?)
其次,使用低通滤波器来帮助剔除不相关的更新。在传感器的Android文档中应该有这样的一个例子。
发布于 2013-07-17 07:50:27
UI非常慢,有时会陷入困境,因为您可能在主UI线程中做了一些事情,花费了很多时间。
不能在doInBackground()方法中更新UI。建议在这种方法中做一些事情需要花费大量的时间。例如,下载数据或图片。
准备好数据后,将调用onPostExecute(),您可以在这些方法中更新UI。
++++++++++++++++++++++++++++++++++++++
你的AsyncTask是正确的。
您不能在doInBackground()中更新UI。因此,两种箭头更新方法都不能应用到doInBackground()方法中.
事实上,我认为您没有必要使用AsyncTask。
问题是,onSensorChanged()在1s中调用350次,而您在其中做的工作太多了。
SensorManager.getRotationMatrix(RotationM, I, gravity, magneticField);
SensorManager.getOrientation(RotationM, direction);如果startFlag是真的:
dataCollector.saveDataShowPath(acceleration, magneticField, startTime, currentTime);您可以像这样更改onSensorChanged()方法:
public void onSensorChanged(SensorEvent event) {
compassChangedTimes++;
if (compassChangedTimes % 50 == 0) {
if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
magneticField[0] = event.values[0];
magneticField[1] = event.values[1];
magneticField[2] = event.values[2];
SensorManager.getRotationMatrix(RotationM, I, gravity, magneticField);
SensorManager.getOrientation(RotationM, direction);
new ArrowheadUpdater(arrowhead).execute(direction[0]);
}
if (startFlag)
dataCollector.saveDataShowPath(acceleration, magneticField, startTime, currentTime);
}
}https://stackoverflow.com/questions/17693967
复制相似问题