2014-02-26 58 views
2

我有一个使用旋转矢量传感器的Android应用程序。在阅读here的评论后,我发现使用更新的API级别,SensorEvent包含4-5个值,而不是3.我的代码使用arraycopy作为数据。如何确保RotationVector的Android API级别兼容?

lastRotVal是一个类成员,并初始化为一个大小为[3]的数组。这是代码响应传感器事件的相关部分。

public void onSensorChanged(SensorEvent event) { 
    System.arraycopy(event.values, 0, lastRotVal, 0, 3); //Hardcoded size 
    lastRotValSet = true; 
    updateDisplay(event); 
} 

private void updateDisplay(SensorEvent event){ 

    if (lastRotValSet){ 
     float[] rotation = new float[9]; 
     float[] orientation = new float[3]; 

     SensorManager.getRotationMatrixFromVector(rotation, lastRotVal); 
     SensorManager.getOrientation(rotation, orientation); 
     double pitch = orientation[1]; 
     double roll = orientation[2]; 

     //Do stuff with roll and pitch 
    } 
} 

我硬编码只能在arraycopy使用3倍的值。它似乎在旧API和新API级别上都有效。这是维持版本之间兼容性的最佳方式,还是我可以做得更好?

编辑:正如接受的答案所述,引发此问题的IllegalArgumentException显然是由于三星设备上的API中的错误而导致的,而不是普通的API版本。所以我会添加一个事实,即我在三星Galaxy SIII上遇到的最初错误。

+0

有趣 - 你看到这个错误的Galaxy S3的变种?我没有遇到过使用Android 4.3的美国Sprint SPH-L710。 –

回答

3

The post你提到的实际上是指在几个三星设备(Galaxy S4,Galaxy Note 3)中的错误 - 请参阅this Android Developer list post。实际上,您不必在SDK级别之间进行任何特殊处理,以使此代码可以在普通设备上工作。但是,唉,碎片...

Chromium handles this issue通过截断数组,如果大小大于4:

if (values.length > 4) { 
    // On some Samsung devices SensorManager.getRotationMatrixFromVector 
    // appears to throw an exception if rotation vector has length > 4. 
    // For the purposes of this class the first 4 values of the 
    // rotation vector are sufficient (see crbug.com/335298 for details). 
    if (mTruncatedRotationVector == null) { 
     mTruncatedRotationVector = new float[4]; 
    } 
    System.arraycopy(values, 0, mTruncatedRotationVector, 0, 4); 
     getOrientationFromRotationVector(mTruncatedRotationVector); 
} else { 
    getOrientationFromRotationVector(values); 
} 

然而,我在我的应用程序GPSTest发现,这种解决方案似乎没有去努力Galaxy S3(参见Github问题here)。

所以,我最终只截断了抛出IllegalArgumentException的设备上的数组。这也避免了额外的System.arraycopy(),除非它是绝对必要的。

下面是代码片段(它也支持API级别小于姜饼的设备上的方向传感器(即,之前,在引入ROTATION_VECTOR传感器时),并处理重新映射为取向的改变的坐标系统),其使用被初始化为false类成员mTruncateVector

@TargetApi(Build.VERSION_CODES.GINGERBREAD) 
@Override 
public void onSensorChanged(SensorEvent event) { 

    double orientation = Double.NaN; 
    double tilt = Double.NaN; 

    switch (event.sensor.getType()) { 
     case Sensor.TYPE_ROTATION_VECTOR: 
      // Modern rotation vector sensors 
      if (!mTruncateVector) { 
       try { 
        SensorManager.getRotationMatrixFromVector(mRotationMatrix, event.values); 
       } catch (IllegalArgumentException e) { 
        // On some Samsung devices, an exception is thrown if this vector > 4 (see #39) 
        // Truncate the array, since we can deal with only the first four values 
        Log.e(TAG, "Samsung device error? Will truncate vectors - " + e); 
        mTruncateVector = true; 
        // Do the truncation here the first time the exception occurs 
        getRotationMatrixFromTruncatedVector(event.values); 
       } 
      } else { 
       // Truncate the array to avoid the exception on some devices (see #39) 
       getRotationMatrixFromTruncatedVector(event.values); 
      } 

      int rot = getWindowManager().getDefaultDisplay().getRotation(); 
      switch (rot) { 
       case Surface.ROTATION_0: 
        // No orientation change, use default coordinate system 
        SensorManager.getOrientation(mRotationMatrix, mValues); 
        // Log.d(TAG, "Rotation-0"); 
        break; 
       case Surface.ROTATION_90: 
        // Log.d(TAG, "Rotation-90"); 
        SensorManager.remapCoordinateSystem(mRotationMatrix, SensorManager.AXIS_Y, 
          SensorManager.AXIS_MINUS_X, mRemappedMatrix); 
        SensorManager.getOrientation(mRemappedMatrix, mValues); 
        break; 
       case Surface.ROTATION_180: 
        // Log.d(TAG, "Rotation-180"); 
        SensorManager 
          .remapCoordinateSystem(mRotationMatrix, SensorManager.AXIS_MINUS_X, 
            SensorManager.AXIS_MINUS_Y, mRemappedMatrix); 
        SensorManager.getOrientation(mRemappedMatrix, mValues); 
        break; 
       case Surface.ROTATION_270: 
        // Log.d(TAG, "Rotation-270"); 
        SensorManager 
          .remapCoordinateSystem(mRotationMatrix, SensorManager.AXIS_MINUS_Y, 
            SensorManager.AXIS_X, mRemappedMatrix); 
        SensorManager.getOrientation(mRemappedMatrix, mValues); 
        break; 
       default: 
        // This shouldn't happen - assume default orientation 
        SensorManager.getOrientation(mRotationMatrix, mValues); 
        // Log.d(TAG, "Rotation-Unknown"); 
        break; 
      } 
      orientation = Math.toDegrees(mValues[0]); // azimuth 
      tilt = Math.toDegrees(mValues[1]); 
      break; 
     case Sensor.TYPE_ORIENTATION: 
      // Legacy orientation sensors 
      orientation = event.values[0]; 
      break; 
     default: 
      // A sensor we're not using, so return 
      return; 
    } 
} 

@TargetApi(Build.VERSION_CODES.GINGERBREAD) 
private void getRotationMatrixFromTruncatedVector(float[] vector) { 
    System.arraycopy(vector, 0, mTruncatedRotationVector, 0, 4); 
    SensorManager.getRotationMatrixFromVector(mRotationMatrix, mTruncatedRotationVector); 
} 

onResume()注册传感器:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { 
     // Use the modern rotation vector sensors 
     Sensor vectorSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR); 
     mSensorManager.registerListener(this, vectorSensor, 16000); // ~60hz 
    } else { 
     // Use the legacy orientation sensors 
     Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION); 
     if (sensor != null) { 
      mSensorManager.registerListener(this, sensor, 
        SensorManager.SENSOR_DELAY_GAME); 
     } 
    } 

全面实施是here on Github

+1

我没有意识到这是一个与三星设备有关的特定错误,所以这是很好的额外信息。只是碰巧,我必须测试的唯一4.3设备是Galaxy SIII。很高兴知道这不是一个更普遍的问题。 – jranalli

+0

感谢您分享您的代码。 – martinstoeckli

1

简短摘要:假设你不想使用值[3]和值[4],你的代码就好了。

从文档:

values[0]: x*sin(θ/2) 
values[1]: y*sin(θ/2) 
values[2]: z*sin(θ/2) 
values[3]: cos(θ/2) 
values[4]: estimated heading Accuracy (in radians) (-1 if unavailable) 
values[3], originally optional, will always be present from SDK Level 18 onwards. values[4] is a new value that has been added in SDK Level 18. 

如果我读,如果你用SDK 18岁或以上编译权,event.values.length只会更大3。

SensorManager.getRotationMatrixFromVector似乎假定长度为== 3的旋转矢量。我不确定如果传入的旋转向量大于3个元素,那么该函数会发生什么。

如果您需要使用event.values [3]和event.values [4],您可以通过检查event.values.length来检测设备是否支持这些扩展值。您也可以在运行时检查Build.VERSION.SDK_INT是否> = 18。但是,如果你不需要它,那么坚持你的硬编码假设3。

+0

我查看了getRotationMatrixFromVector的源代码,它只提取前3个值,所以没有区别。谁知道未来会发生什么。关于为什么event.values.length在这种情况下不起作用,请参阅接受的答案。这只是三星设备的一些奇怪的错误。 – jranalli