android - 如何正确使用 AsyncTask 以避免 UI 变慢?

标签 android android-asynctask android-ui

在我的 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/

相关文章:

Android:从服务中的 onPostexecute AsyncTask 获取结果

android - 通过 AsyncTask 在 onCreate() 期间加载数据

android - 通过 IClipboardDataPasteEventImpl 内存泄漏

Android- 如何区分 Galaxy S-3 和 Galaxy S-4 布局?

java - Android 游戏中的列表迭代器

android - 上传文件时 MultipartEntityBuilder 中出现 NoClassDefFoundError

android - loader 和 AsyncTask 有什么区别?

Android Orientation change WebView, Cookies need saving

java - 如何返回 DocumentSnapShot 作为方法的结果?

android - 带有附加垂直填充的 BackgroundColorSpan