在我的 UI 上,我有一个需要实时更新的 ImageView
(箭头图像)。
箭头有两种可能的移动方式:
- 旋转以始终指向北方
- 在用户位置改变时移动
我已经掌握了这两种方法来正常运行。
唯一的问题是我的UI 非常慢,有时会卡住。与此同时,我的手机在应用程序运行时总是变得非常热。 Logcat
有时也会告诉我
Skipped * frames. The application may be doing too much work on its main thread.
我被告知使用 AsyncTask
以免对我的 UI 造成压力。所以我在 AsyncTask
中进行箭头更新工作。但是,问题仍然存在。我的用户界面仍然很慢。
我想我的 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);
}
我应该将 2 个箭头更新方法放在 doInBackground()
中而不是 onPostExecute()
中吗?
但是 doInBackground() 中的行可以更新 UI 吗?我不确定。
我的AsyncTask
有什么问题吗?
欢迎提出其他猜测或意见!
更多线索:
我只是注意到,当我刚开始这个 Activity 时,UI 非常慢并且经常卡住。但过了一会儿,比如 10 秒,它变得有些平滑,可以接受。
最佳答案
AsyncTasks 用于在后台执行繁重/冗长的操作,并将结果推送到 UI。
在您的情况下,您的 AsyncTask 没有执行繁重的操作,因此您可能可以丢弃它。
此外,您当前的代码对 UI 更新的频率没有限制。您可以使用处理程序实现此类限制。
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 度已经足够精确,您甚至可能想在那里设置更大的值)。
关于android - 如何正确使用 AsyncTask 以避免 UI 变慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17693967/