在给拍照文件添加EXIF信息过程中,发现一个奇怪的问题:可以添加物距信息,但不能添加焦距信息。查看源码,发现走的代码不同:
- /*setAttribute()*/
- // Convert the given value to rational values for backwards compatibility.
- if (value != null && sTagSetForCompatibility.contains(tag)) {
- if (tag.equals(TAG_GPS_TIMESTAMP)) {
- Matcher m = sGpsTimestampPattern.matcher(value);
- if (!m.find()) {
- Log.w(TAG, "Invalid value for " + tag + " : " + value);
- return;
- }
- value = Integer.parseInt(m.group(1)) + "/1," + Integer.parseInt(m.group(2)) + "/1,"
- + Integer.parseInt(m.group(3)) + "/1";
- } else {
- try {
- double doubleValue = Double.parseDouble(value);
- value = (long) (doubleValue * 10000L) + "/10000";
- } catch (NumberFormatException e) {
- Log.w(TAG, "Invalid value for " + tag + " : " + value);
- return;
- }
- }
- }
物距信息与镜头焦距信息的tag分别是
ExifInterface.TAG_SUBJECT_DISTANCE,ExifInterface.TAG_FOCAL_LENGTH
前者在sTagSetForCompatibility中,后者则不在
- private static final HashSet
sTagSetForCompatibility = new HashSet<>(Arrays.asList( - TAG_F_NUMBER, TAG_DIGITAL_ZOOM_RATIO, TAG_EXPOSURE_TIME, TAG_SUBJECT_DISTANCE,
- TAG_GPS_TIMESTAMP));
因此,一种解决方案是,使用反射,把焦距信息的tag加入到sTagSetForCompatibility中
- static {//解决设置ExifInterface.TAG_FOCAL_LENGTH失败问题
- try {
- Field field = ExifInterface.class.getDeclaredField("sTagSetForCompatibility");
- field.setAccessible(true);
- HashSet
set = (HashSet) field.get(null); - Objects.requireNonNull(set).add(ExifInterface.TAG_FOCAL_LENGTH);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
使用静态代码块确保在设置EXIF信息之前的调用,且只调用一次。
那么为什么之前设置EXIF信息无效呢,接着看源码。
- Pair
guess = guessDataFormat(value); - int dataFormat;
- if (exifTag.primaryFormat == guess.first || exifTag.primaryFormat == guess.second) {
- dataFormat = exifTag.primaryFormat;
- } else if (exifTag.secondaryFormat != -1 && (exifTag.secondaryFormat == guess.first
- || exifTag.secondaryFormat == guess.second)) {
- dataFormat = exifTag.secondaryFormat;
- } else if (exifTag.primaryFormat == IFD_FORMAT_BYTE
- || exifTag.primaryFormat == IFD_FORMAT_UNDEFINED
- || exifTag.primaryFormat == IFD_FORMAT_STRING) {
- dataFormat = exifTag.primaryFormat;
- } else {
- if (DEBUG) {
- Log.d(TAG, "Given tag (" + tag
- + ") value didn't match with one of expected "
- + "formats: " + IFD_FORMAT_NAMES[exifTag.primaryFormat]
- + (exifTag.secondaryFormat == -1 ? "" : ", "
- + IFD_FORMAT_NAMES[exifTag.secondaryFormat]) + " (guess: "
- + IFD_FORMAT_NAMES[guess.first] + (guess.second == -1 ? "" : ", "
- + IFD_FORMAT_NAMES[guess.second]) + ")");
- }
- continue;
- }
之前的设置焦距信息走的是else分支,不往下走了,原因是value的类型不对。
new ExifTag(TAG_FOCAL_LENGTH, 37386, IFD_FORMAT_URATIONAL),//ExifInterface.IFD_EXIF_TAGS
exifTag.primaryFormat 的类型是IFD_FORMAT_URATIONAL,也就是无符号有理数,而之前的
guessDataFormat()中,不考虑多值拼接的话,则是
- /*guessDataFormat()*/
- try {
- Long longValue = Long.parseLong(entryValue);
- if (longValue >= 0 && longValue <= 65535) {
- return new Pair<>(IFD_FORMAT_USHORT, IFD_FORMAT_ULONG);
- }
- if (longValue < 0) {
- return new Pair<>(IFD_FORMAT_SLONG, -1);
- }
- return new Pair<>(IFD_FORMAT_ULONG, -1);
- } catch (NumberFormatException e) {
- // Ignored
- }
- try {
- Double.parseDouble(entryValue);
- return new Pair<>(IFD_FORMAT_DOUBLE, -1);
- } catch (NumberFormatException e) {
- // Ignored
- }
- return new Pair<>(IFD_FORMAT_STRING, -1);
里面的返回值并没有IFD_FORMAT_URATIONAL!所以,只要value是带小数点的float或double类型,且tag没有在之前的sTagSetForCompatibility中,均会设置失败,不知这算不算系统代码的bug。而tag在sTagSetForCompatibility中的,则是转成了String类型。