2

在我的 UI 上,我有一个ImageView(箭头图像)需要实时更新。

箭头有 2 种可能的移动方式:

  • 旋转始终指向北方
  • 当用户的位置改变时移动

我已经让这两种方法正常运行。

唯一的问题是我的UI 很慢,有时会卡住。同时,我的手机在运行应用程序时总是变得非常热。Logcat有时也告诉我

跳过*帧。应用程序可能在其主线程上做了太多工作。

我被告知要使用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吗?

欢迎其他猜测或评论!

更多线索:

我只是注意到,当我刚刚进入这个活动时,用户界面非常慢并且卡住了很多。但过了一段时间,比如 10 秒,它有点变得可以接受的平滑。

4

4 回答 4

2

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 将使用最新的角度值更新,并且不会尝试显示所有中间阶段。

此外,差异测试避免了不必要的更新(我假设 1 度足够精确,您甚至可能希望在此处设置更大的值)。

于 2013-07-17T08:13:25.460 回答
1

您的问题是您每秒收到 350 次对该函数的调用,而忽略“垃圾”数据的工作做得不好。

首先,将尽可能多的逻辑移动到您的compassChangedTimes条件中(此外,您应该重置该计数以避免溢出)。更好的是,决定是否根据传感器变化阈值而不是任意数量的样本来更新 UI(如果传感器报告一个恒定值,并且该onChangeEvent()函数被偶尔调用会发生什么情况?)

其次,使用低通滤波器来帮助去除不相关的更新。传感器的 Android API 文档中应该有这样的示例。

于 2013-07-17T07:55:16.607 回答
0

UI 非常慢,有时会卡住,因为您可能在主 UI 线程中做了一些花费大量时间的事情。

您无法在doInBackground()方法中更新 UI。建议在这种方法中做某事需要很多时间。例如,下载数据或图片。

数据准备好后,onPostExecute()将被调用,您可以在这些方法中更新您的 UI。

++++++++++++++++++++++++++++++++++++++++++

您的 AsyncTask 是正确的。

您不能在 doInBackground() 中更新 UI。所以,2种箭头更新方法不能放入doInBackground()方法中。

其实我觉得你没必要用AsyncTask。

问题是onSensorChanged()在 1 秒内调用 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);
        }
    }
于 2013-07-17T07:50:27.737 回答
0

上面有很好的答案.. 但是异步任务有很多漏洞,我在关注robo-spice 应用程序后才知道。

该应用程序是开源的。看看它 。

于 2013-07-18T05:45:06.323 回答