【问题标题】:SensorEvent onChanged Multithreading?SensorEvent onChanged 多线程?
【发布时间】:2013-09-20 18:57:29
【问题描述】:

我正在检测设备的方向以检查它是否倒置在表面上,然后在其上调用方法。我遇到的问题是,用户可能会无意中将设备的方向转向该位置一瞬间并返回一个面朝上的位置以纠正他们的错误,但是在他们第一次这样做时仍然会调用该方法。

我正在尝试以 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 以执行双重检查?

【问题讨论】:

    标签: android delay accelerometer orientation-changes sensormanager


    【解决方案1】:

    一种非常简单的方法是让传感器有机会在接下来的 n 毫秒内(正如所要求的那样)给出一个新值:

    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- 只要你的 if 语句计算结果为真,就发布这个可运行文件;但是,请在 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 语句来删除可运行对象。

    @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%,其余的是最后四个值的平均值。

      尝试使用 coefs 直到您对结果感到满意为止。

      对我来说效果很好:

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

      编辑

      这是初始化 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
              }
       }
      

      【讨论】:

      • “可能丢失精度。发现:双精度要求:浮点数”
      • 是的,将 z_value 类型更改为 double ,因为 event.values 是双精度数
      • "变量 z_value 可能尚未初始化" 我不知道该怎么做,但我真的很想试试你的解决方案。
      • 是的,这是真的,我在这里犯了一个错误,z_value 应该是一个字段,以便在其中保留平均值,我现在正在编辑
      • 我试过了,我看不出有什么不同。问题仍然存在。虽然,这可能对我正在进行的另一个项目有所帮助。
      【解决方案3】:

      另一种方法是随着时间的推移平滑输入,因此取 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);
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-01-28
        • 1970-01-01
        • 2019-11-16
        • 2012-05-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多