在给拍照文件添加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<String> 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<String> set = (HashSet<String>) field.get(null);
Objects.requireNonNull(set).add(ExifInterface.TAG_FOCAL_LENGTH);
} catch (Exception e) {
e.printStackTrace();
}
}
使用静态代码块确保在设置EXIF信息之前的调用,且只调用一次。
那么为什么之前设置EXIF信息无效呢,接着看源码。
Pair<Integer, Integer> 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类型。