2014-02-26 61 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