2014-02-26 2 views
2

로테이션 벡터 센서를 사용하는 Android 앱이 있습니다. 덧글 here을 읽었을 때 새로운 API 레벨에서 SensorEvent에 3 대신 4 ~ 5 개의 값이 포함 된 것을 볼 수 있습니다. 내 코드는 데이터에 대해 배열 복사를 사용합니다.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의 버그의 결과로 발생한다. 그래서 저는 삼성 갤럭시 SIII에서 초기 오류가 발생했다는 사실을 추가 할 것입니다.

+0

흥미 롭습니다 -이 오류가 Galaxy S3의 어떤 변형에 나타 났습니까? Android 4.3이 포함 된 미국의 Sprint 변형 SPH-L710에서는이 문제가 발생하지 않았습니다. –

답변

3

The post 참조하는 것은 실제로 일부 삼성 장치 (Galaxy S4, Galaxy Note 3)의 버그를 나타냅니다 (this Android Developer list post 참조). 이 코드가 일반 장치에서 작동하려면 SDK 수준간에 특별한 처리를하지 않아도됩니다. 크기가 큰 4보다 클 경우, 배열을 절단함으로써하지만, 슬프게도, 조각 ...

Chromium handles this issue 그러나

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에서 발견 갤럭시 S3 (Github issue here 참조).

따라서, IllegalArgumentException을 던지는 장치에서만 배열을 자르려고했습니다. 또한 절대적으로 필요한 경우가 아니면 추가 System.arraycopy()를 피할 수 있습니다.

다음은 코드 레벨입니다 (API 레벨이 진저 브레드보다 작은 기기에서 방향 센서도 지원합니다.

@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() 상기 센서 등록 :

등) ROTATION_VECTOR 센서가 도입되었을 때 이전 및 false 초기화된다 반원 mTruncateVector을 사용하여 방향 변경 좌표계)를 리매핑 핸들
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의 소스 코드를 살펴 보았을 때 처음 세 개의 값만 가져 오므로 차이가 없습니다. 미래에 어떤 일이 일어날 지 누가 알겠습니까? 이 경우 event.values.length가 작동하지 않는 이유에 대한 대답을 확인하십시오. 삼성 기기에 이상한 버그 일뿐입니다. – jranalli