2013-07-17 2 views
2

В моем пользовательском интерфейсе у меня есть ImageView (изображение стрелки), которое необходимо обновить в режиме реального времени.Как правильно использовать AsyncTask, чтобы избежать медленного интерфейса?

Есть 2 возможные перемещения наконечника стрелы:

  • вращающиеся всегда указывают на Северный
  • перемещение, когда местоположение пользователя изменилось

я уже получил эти 2 метода работает должным образом.

Проблема только в том, что мой пользовательский интерфейс очень медленный и иногда застревает. Между тем, мой телефон всегда становится очень горячим, пока приложение запускается на нем. Logcat также иногда говорит мне

Пропущенные * кадров. Приложение может делать слишком много работы над своей основной темой .

Мне сказали использовать AsyncTask, чтобы не подчеркивать свой интерфейс. Поэтому я занимаюсь обновлением своей стрелки в 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() обновлять пользовательский интерфейс? Я не уверен.

Есть ли что-то не так в моем AsyncTask?

Другие догадки или комментарии приветствуются!

БОЛЬШЕ ПОДСКАЗКИ:

Я просто заметил, что, когда я просто попасть в эту деятельность, интерфейс супер медленно и застревает много. Но через некоторое время, скажем, 10 секунд, он несколько становится приемлемо гладким.

ответ

2

AsyncTasks используются для выполнения тяжелых/длинных операций в фоновом режиме и результата, наносимого на пользовательский интерфейс.

В вашем случае ваша AsyncTask не выполняет тяжелую операцию, поэтому вы, вероятно, можете ее отбросить.

Кроме того, в вашем текущем кодексе нет ограничений по частоте обновлений пользовательского интерфейса. Вы можете реализовать такой предел с помощью 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); 
    } 
} 

Принцип заключается в том, что вы отправляете сообщения в обработчик, но обработчик будет только выбрать первое сообщение и отказаться от остальных (это также можно проверить обработчик, чтобы узнать, есть ли уже какие-либо сообщения, прежде чем отправлять , Я не уверен, что более эффективно).

Таким образом, пользовательский интерфейс будет обновлен с последним значением угла и не будет показывать все промежуточные этапы.

Кроме того, тест diff позволяет избежать ненужных обновлений (я предполагаю, что 1 градус достаточно точен, вы можете даже захотеть поставить там большую ценность).

+0

Спасибо за идею! Но в чем разница между использованием обработчика и прямым вызовом метода? Новичок, пожалуйста, помогите объяснить. –

+0

также, возможно ли мне поставить 'SensorManager.getRotationMatrix (RotationM, I, gravity, magnetField); SensorManager.getOrientation (RotationM, direction); 'from' onSensorChanged()' в 'doInBackGround', поскольку, как вы сказали,' doInBackGround' предназначен для тяжелых вычислений? –

+0

отправка сообщения обработчику означает, что задание будет выполнено в какой-то момент. это не значит, что это должно быть прямо сейчас, и это означает, что вы можете отправлять десятки сообщений тем временем. это дает большую гибкость планированию. В принципе, вы просто говорите: «значение имеет изменения, пожалуйста, обновите интерфейс в какой-то момент». (задержка обычно составляет всего несколько 100 миллисовских вершин, хотя) – njzk2

0

Пользовательский интерфейс очень медленный и иногда застревает, потому что вы, возможно, что-то занимаете много времени в основной теме пользовательского интерфейса.

Вы не можете обновить интерфейс пользователя в методе doInBackground(). Предполагается, что в этом методе что-то займет много времени. Например, загрузка данных или изображение.

После того, как данные будут подготовлены, будет называться onPostExecute(), вы можете обновить свой интерфейс в этом методе.

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

Ваш AsyncTask правильно ,

Вы не можете обновить интерфейс в doInBackground(). Таким образом, два метода обновления стрелок не могут быть введены в метод doInBackground().

На самом деле, я думаю, вам не нужно использовать AsyncTask.

Проблема onSensorChanged() вызывает 350 раз за 1 с, и вы слишком много работаете в ней.

 SensorManager.getRotationMatrix(RotationM, I, gravity, magneticField); 
     SensorManager.getOrientation(RotationM, direction); 

и если верно нач-флаг:

 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); 
     } 
    } 
+0

Можно ли взглянуть на то, что я сделал подробно? Такой общий ответ вряд ли поможет. :) –

+0

Значит, вы имеете в виду, что улучшения в моей AsyncTask не могут быть улучшены?Моя проблема не там? –

+0

Я думаю, вы должны изменить метод onSensorChanged(). – srain

1

Ваша проблема заключается в том, что вы получаете 350 звонков в том, что функция вторая, и вы не делаете хорошую работу, игнорируя данные «мусор».

Во-первых, переместите столько логики, сколько сможете, в свое состояние compassChangedTimes (также вы должны сбросить этот счет, чтобы избежать переполнения). Еще лучше решить, следует ли обновлять пользовательский интерфейс на основе порога изменения датчика, а не произвольного количества выборок (что произойдет, если датчик сообщает о постоянном значении, а функция onChangeEvent() называется спорадически?)

Во-вторых, используйте фильтр нижних частот, чтобы исключить ненужные обновления. В документации API Android для датчиков должен быть приведен пример.

0

Есть хорошие ответы выше .. Но Асинхронная задача имеет много лазейки, которую я узнал после того, как после robo-spice app.

приложения с открытым исходным кодом. Посмотри на это .

Смежные вопросы