• 解决android中 ExifInterface.setAttribute()无效问题


    在给拍照文件添加EXIF信息过程中,发现一个奇怪的问题:可以添加物距信息,但不能添加焦距信息。查看源码,发现走的代码不同:

    1. /*setAttribute()*/
    2. // Convert the given value to rational values for backwards compatibility.
    3. if (value != null && sTagSetForCompatibility.contains(tag)) {
    4. if (tag.equals(TAG_GPS_TIMESTAMP)) {
    5. Matcher m = sGpsTimestampPattern.matcher(value);
    6. if (!m.find()) {
    7. Log.w(TAG, "Invalid value for " + tag + " : " + value);
    8. return;
    9. }
    10. value = Integer.parseInt(m.group(1)) + "/1," + Integer.parseInt(m.group(2)) + "/1,"
    11. + Integer.parseInt(m.group(3)) + "/1";
    12. } else {
    13. try {
    14. double doubleValue = Double.parseDouble(value);
    15. value = (long) (doubleValue * 10000L) + "/10000";
    16. } catch (NumberFormatException e) {
    17. Log.w(TAG, "Invalid value for " + tag + " : " + value);
    18. return;
    19. }
    20. }
    21. }

    物距信息与镜头焦距信息的tag分别是

    ExifInterface.TAG_SUBJECT_DISTANCE,ExifInterface.TAG_FOCAL_LENGTH

    前者在sTagSetForCompatibility中,后者则不在

    1. private static final HashSet sTagSetForCompatibility = new HashSet<>(Arrays.asList(
    2. TAG_F_NUMBER, TAG_DIGITAL_ZOOM_RATIO, TAG_EXPOSURE_TIME, TAG_SUBJECT_DISTANCE,
    3. TAG_GPS_TIMESTAMP));

    因此,一种解决方案是,使用反射,把焦距信息的tag加入到sTagSetForCompatibility中

    1. static {//解决设置ExifInterface.TAG_FOCAL_LENGTH失败问题
    2. try {
    3. Field field = ExifInterface.class.getDeclaredField("sTagSetForCompatibility");
    4. field.setAccessible(true);
    5. HashSet set = (HashSet) field.get(null);
    6. Objects.requireNonNull(set).add(ExifInterface.TAG_FOCAL_LENGTH);
    7. } catch (Exception e) {
    8. e.printStackTrace();
    9. }
    10. }

    使用静态代码块确保在设置EXIF信息之前的调用,且只调用一次。

    那么为什么之前设置EXIF信息无效呢,接着看源码。

    1. Pair guess = guessDataFormat(value);
    2. int dataFormat;
    3. if (exifTag.primaryFormat == guess.first || exifTag.primaryFormat == guess.second) {
    4. dataFormat = exifTag.primaryFormat;
    5. } else if (exifTag.secondaryFormat != -1 && (exifTag.secondaryFormat == guess.first
    6. || exifTag.secondaryFormat == guess.second)) {
    7. dataFormat = exifTag.secondaryFormat;
    8. } else if (exifTag.primaryFormat == IFD_FORMAT_BYTE
    9. || exifTag.primaryFormat == IFD_FORMAT_UNDEFINED
    10. || exifTag.primaryFormat == IFD_FORMAT_STRING) {
    11. dataFormat = exifTag.primaryFormat;
    12. } else {
    13. if (DEBUG) {
    14. Log.d(TAG, "Given tag (" + tag
    15. + ") value didn't match with one of expected "
    16. + "formats: " + IFD_FORMAT_NAMES[exifTag.primaryFormat]
    17. + (exifTag.secondaryFormat == -1 ? "" : ", "
    18. + IFD_FORMAT_NAMES[exifTag.secondaryFormat]) + " (guess: "
    19. + IFD_FORMAT_NAMES[guess.first] + (guess.second == -1 ? "" : ", "
    20. + IFD_FORMAT_NAMES[guess.second]) + ")");
    21. }
    22. continue;
    23. }

    之前的设置焦距信息走的是else分支,不往下走了,原因是value的类型不对。

    new ExifTag(TAG_FOCAL_LENGTH, 37386, IFD_FORMAT_URATIONAL),//ExifInterface.IFD_EXIF_TAGS
    exifTag.primaryFormat 的类型是IFD_FORMAT_URATIONAL,也就是无符号有理数,而之前的
    guessDataFormat()中,不考虑多值拼接的话,则是
    1. /*guessDataFormat()*/
    2. try {
    3. Long longValue = Long.parseLong(entryValue);
    4. if (longValue >= 0 && longValue <= 65535) {
    5. return new Pair<>(IFD_FORMAT_USHORT, IFD_FORMAT_ULONG);
    6. }
    7. if (longValue < 0) {
    8. return new Pair<>(IFD_FORMAT_SLONG, -1);
    9. }
    10. return new Pair<>(IFD_FORMAT_ULONG, -1);
    11. } catch (NumberFormatException e) {
    12. // Ignored
    13. }
    14. try {
    15. Double.parseDouble(entryValue);
    16. return new Pair<>(IFD_FORMAT_DOUBLE, -1);
    17. } catch (NumberFormatException e) {
    18. // Ignored
    19. }
    20. return new Pair<>(IFD_FORMAT_STRING, -1);

    里面的返回值并没有IFD_FORMAT_URATIONAL!所以,只要value是带小数点的float或double类型,且tag没有在之前的sTagSetForCompatibility中,均会设置失败,不知这算不算系统代码的bug。而tag在sTagSetForCompatibility中的,则是转成了String类型。

  • 相关阅读:
    HC32_HC32F072FAUA_FLASH使用
    点成分享 | 微流控技术集成系统的应用
    【探索Linux】P.39(传输层 —— TCP的三次 “握手” 和四次 “挥手” )
    【Windows 常用工具系列 13 -- Confluence 如何快速输入代码块 code block】
    SystemVerilog-决策语句-case语句
    单调对列刷题
    2.5 Go语言中的切片(Slice)
    1373. 二叉搜索子树的最大键值和
    RocketMQ概论
    JAVA小游戏 “拼图”
  • 原文地址:https://blog.csdn.net/wlxyhy/article/details/126243924