3

Я определяю ориентацию устройства, чтобы проверить, помещен ли он вверх дном на поверхность, а затем вызывает метод. Проблема, с которой я столкнулся, заключается в том, что пользователь может непреднамеренно переключить ориентацию устройства на эту позицию на долю секунды и вернуть лицевую позицию для исправления их ошибки, однако метод все еще называется первым, когда они это делают.SensorEvent onChanged Multithreading?

Я пытаюсь добавить «двойную проверку» к ориентации с задержкой в ​​800 мс, чтобы у пользователя было время исправить ориентацию до того, как этот метод будет запущен. К сожалению, я не могу контролировать, как часто вызывается onSensorChanged, чтобы добавить поток с задержкой в ​​800 мс, чтобы дважды проверить ориентацию, чтобы предотвратить непреднамеренное изменение ориентации.

Мой код выглядит следующим образом,

public SensorEventListener accelerometerListener = new SensorEventListener(){ 

    @Override 
    public void onSensorChanged(SensorEvent event) { 

      float z_value = event.values[2]; 

       if (z_value < 0) { 
        //the device is now upside-down 

         try { 

          Thread.sleep(800); 

          //this if statement is never called correctly because 
          // onSensorChanged is not called again. 
           if (z_value < 0) { 
            toggleMethod(); 
           } 

         } catch (InterruptedException e) { 
           e.printStackTrace(); 
         } 
       } 
      } 

Моим вопрос: Есть ли способ, я могу назвать onSensorChanged на задержке в пределах самого onSensorChanged метода для того, чтобы преформ двойной проверки?

ответ

2

Очень простой подход должен был бы дать шанс для датчика, чтобы дать новое значение в предстоящих п миллисекунд (так же, как просили) будет следующим:

1- Определите, сколько миллисекунд вы хотите, чтобы дать пользователю, чтобы отменить его действие (вы использовали 800):

private static final long TIME_MARGIN_FOR_USER_TO_UNDO = 800; 

2- Создайте обработчик и выполнимый для выполнения действия (пусть они будут иметь больший объем, чем ваш onSensorChanged - например, Ваша деятельность):

Handler sensorHandler = new Handler(); 
Runnable toggleRunnable = new Runnable() { 
    public void run() { 
     toggleMethod(); 
    } 
} 

3- сообщение это работоспособное всякий раз, когда ваше заявление, если имеет значение истинно; Однако опубликуйте его после n миллисекунд.

@Override 
public void onSensorChanged(SensorEvent event) { 
    float z_value = event.values[2]; 
    if (z_value < 0) { 
     sensorHandler.postDelayed(toggleRunnable, TIME_MARGIN_FOR_USER_TO_UNDO); 
    } 
} 

4- Поскольку onSensorChanged будет вызываться, когда значение датчика изменяется, вы можете остановить работоспособный от запуска, если пользователь установил ее. Поэтому вам понадобится инструкция else, которая удалит runnable.

@Override 
public void onSensorChanged(SensorEvent event) { 
    float z_value = event.values[2]; 
    if (z_value < 0) { 
     sensorHandler.postDelayed(toggleRunnable, TIME_MARGIN_FOR_USER_TO_UNDO); 
    } 
    else { 
     sensorHandler.removeCallbacks(toggleRunnable); 
    } 
} 
2

Лучшее, что вы делаете, когда используете onSensorCheck, - это использовать фильтр: вы всегда принимаете решение в среднем по n последним значениям, заданным датчиком, таким образом ваше приложение будет вести себя плавно, а внезапные изменения сенсора не будут влияют на него.
Вот как это сделать:

z_value = 0.2*event.values[2]+0.8*z_value; // 0.2+0.8 = 1 

таким образом, вы принимаете только 20% от вашего нового значения, а остальное представляет собой среднее из четырех последних значений.

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

для меня это работало достаточно хорошо:

z_value = 0.02*event.values[2]+0.98*z_value; 

--------------------------------------------------------------- 

EDIT

Вот как сделать, чтобы инициализировать z_value: z_value должно быть поле, нужно добавить еще одно логическое поле, используемое только для инициализации, то есть для первого вызова onSensorChanged. Так у вас есть два поля:

double z_value =0; 
boolean firstTime = true; 

и вот onSensorChanged метод:

@Override 
public void onSensorChanged(SensorEvent event) { 
     if(firstTime){ 
      z_value = event.values[2]; // you will enter here only the first time 
      firstTime = false 
     } 
     else{ 
      z_value = 0.02*event.values[2] + 0.98*z_value; 
     } 

     if (z_value < 0) { 
       //the device is now upside-down 

       // do what you gotta do 
     } 
} 
+0

«Возможные потери точности Найдено:. Double Обязательно: Поплавок» –

+0

да изменить тип z_value удвоиться, потому что event.values ​​двойники – Turkish

+0

«переменная z_value не может быть инициализирована» Я понятия не имею, что к но я действительно хочу попробовать ваше решение. –

0

Другой подход заключается в сглаживать вход с течением времени, поэтому взять среднее показание над 800 мс ввода.

(Ответ YAT имеет правильную идею, но исполнение фактически не учитывает количество сэмплов, передаваемых с течением времени, которое будет неизвестно с одного телефона на следующий. Оно также никогда не забывает о любых измерениях, поведении после 10 секунд могут существенно отличаться через 1 минуту.)

//the idea here is to store all the inputs over the last 800ms and average them 
LinkedList<Entry<long, double>> inputs = new LinkedList<Entry<long,double>> 
double totalZ = 0.0f; //keep a running total so we don't have to iterate through the list to calculate the average 

@Override 
public void onSensorChanged(SensorEvent event) { 
    //clear out values older than 800ms 
    while(inputs.size() > 0 && inputs.getLast().getKey() < System.currentTimeInMillis() - 800){ 
     Entry<long, double> deletedEntry = inputs.removeLast(); 
     totalZ -= deletedEntry.getValue(); //update the running total 
    } 

    //add the new measurement to the head 
    inputs.addFirst(new Entry<long, double>(System.currentTimeInMillis(),event.values[2])); 
    totalZ += event.values[2]; //update the running total with the new measurement 

    //this is the Z averaged over 800ms 
    float averagedZ = totalZ/inputs.size(); 
} 

Или более простой подход, в соответствии с вопросом спрашивает, при обнаружении первоначального изменения.

Сохранить время, сохранить новое потенциальное состояние

Затем, когда вы получите еще onSensorChanged()

Это состояние так же, как потенциальное состояние? Если это не так, очистите время и очистите состояние потенциалов

Если прошло 800 мс, а потенциальное состояние все еще находится в текущем состоянии, тогда инициируйте это изменение.

onSensorChanged вызывается много раз в секунду в большинстве режимов точности, поэтому вы должны быть близки к вашим 800 мс, ожидая обычного вызова onSensorChanged.

Грубый код псевдо ниже

long lastChange = 0; 
int potentialState = 0; 

@Override 
public void onSensorChanged(SensorEvent event) { 
    int currentState = determineCurrentState(event); //whatever your logic is and states 

    if(potentialState != currentState){ 
     lastChange = System.currentTimeInMillis(); 
     potentialState = currentState; 
    } 
    else if(lastChange + 800 < System.currentTimeInMillis()){ 
     //execute state change, 800ms have elapses with no further change in state 
     stateChange(currentState); 
    } 
Смежные вопросы