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

在给拍照文件添加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类型。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值