2

私の UI にはImageView、リアルタイムで更新する必要がある (矢印の画像) があります。

矢じりには 2 つの可能な動きがあります。

  • 常に北を指すように回転する
  • ユーザーの場所が変わったときの移動

これら2つの方法が正しく機能するようにすでに取得しています。

唯一の問題は、UI が非常に遅く、時々動かなくなることです。一方、アプリを実行している間、私の電話は常に非常に熱くなります。Logcatも時々教えてくれます

*フレームをスキップしました。アプリケーションがメイン スレッドで処理しすぎている可能性があります。

AsyncTaskUIにストレスを与えないように使うように言われました。だから私は矢じりの更新作業を で行い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()の代わりに2 つの矢印更新メソッドを入れる必要がありonPostExecute()ますか?

しかし、doInBackground() の行は UI を更新できますか? 私はわかりません。

私の中に何か問題がありAsyncTaskますか?

他の推測やコメントは大歓迎です!

より多くの手がかり:

このアクティビティに入ると、UI が非常に遅くなり、多くのスタックが発生することに気付きました。しかし、しばらくすると、たとえば 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 は最新の角度値で更新され、すべての中間段階を表示しようとしなくなります。

また、diff テストは不必要な更新を回避します (1 度で十分正確であると仮定します。より大きな値を設定することもできます)。

于 2013-07-17T08:13:25.460 に答える
1

あなたの問題は、その関数に対して 1 秒間に 350 回の呼び出しがあり、「ゴミ」データを無視して良い仕事をしていないことです。

まず、できるだけ多くのロジックをcompassChangedTimes条件に移動します (また、オーバーフローを避けるためにそのカウントをリセットする必要があります)。さらに良いのは、任意のサンプル数ではなく、センサーの変化のしきい値に基づいて UI を更新するかどうかを決定することです (センサーが一定の値を報告し、onChangeEvent()関数が散発的に呼び出されるとどうなるでしょうか?)

次に、ローパス フィルターを使用して、無関係な更新を取り除きます。センサーの Android API ドキュメントに、この例があるはずです。

于 2013-07-17T07:55:16.607 に答える
0

上記の良い答えがあります..しかし、非同期タスクには多くの抜け穴があり、 robo-spiceアプリをフォローした後に知りました。

アプリはオープンソースです。それを見てください。

于 2013-07-18T05:45:06.323 に答える