Skip to content

xml attribute exception

异常信息

由于属性值包含 0x0, 如: <LinearLayout android:layout_gravity="0x0" android:orientation="horizontal" 导致编译时异常, 异常信息如下

java
W: C:\Users\ADMINI~1\AppData\Local\Temp\APKTOOL4940937019646549448-12345\res\layout\cas_ip_activity.xml:11: error: '0x0' is incompatible with attribute layout_gravity (attr) flags [bottom=80, center=17, center_horizontal=1, center_vertical=16, clip_horizontal=8, clip_vertical=128, end=8388613, fill=119, fill_horizontal=7, fill_vertical=112, left=3, right=5, start=8388611, top=48].
W: error: failed linking file resources.

如下图

image-20260810095501409

解决思路

通过修改 apktool 源码, 判断属性值为 0x0 则替换为 "", 改动如下

java/brut/androlib/res/decoder/BinaryXmlResourceParser.java

java
@Override
public String getAttributeValue(int index) {
        Attribute attr = getAttribute(index);
        if (attr == null) {
            return "";
        }

        // Use the raw value if preserved (rare for modern apps).
        if (mHasRawValues && !mIgnoreRawValues) {
            String rawValue = mStringPool != null ? mStringPool.getString(attr.rawValue) : null;
            if (rawValue != null) {
                return rawValue;
            }
        }

        // Try to decode the typed value from the resource table.
        ResItem value = null;
        String name = null;
        String decoded = null;
        try {
            ResPackage pkg = mTable.getMainPackage();
            if (pkg == null) {
                // If no main package, we load "android" package instead.
                pkg = mTable.resolvePackageGroup(ResTable.SYS_PACKAGE_ID).getBasePackage();
            }

            if (attr.valueType == ResValue.TYPE_STRING) {
                CharSequence strValue = mStringPool != null ? mStringPool.getText(attr.valueData) : null;
                value = strValue != null ? new ResString(strValue) : null;
            } else {
                value = ResItem.parse(pkg, attr.valueType, attr.valueData);
            }

            if (value != null) {
                ResId nameId = getAttributeNameResourceId(index);
                if (nameId != ResId.NULL) {
                    // We need the attribute entry's value to format this value.
                    try {
                        ResEntry nameEntry = mTable.resolveEntry(nameId);
                        name = nameEntry.getName();
                        if (nameEntry.getValue() instanceof ResAttribute) {
                            ResAttribute nameValue = (ResAttribute) nameEntry.getValue();

                            // Add the value type to the attribute if needed.
                            boolean isExplicitType;
                            switch (attr.valueType) {
                                case ResValue.TYPE_NULL:
                                case ResValue.TYPE_REFERENCE:
                                case ResValue.TYPE_DYNAMIC_REFERENCE:
                                case ResValue.TYPE_ATTRIBUTE:
                                case ResValue.TYPE_DYNAMIC_ATTRIBUTE:
                                    isExplicitType = false;
                                    break;
                                default:
                                    isExplicitType = true;
                                    break;
                            }
                            if (isExplicitType && !nameValue.hasSymbolsForValue(value)) {
                                nameValue.addValueType(attr.valueType);
                            }

                            decoded = nameValue.formatAsAttributeValue(value);
                            if (decoded.equals("0x0")) {
                                System.out.println("W: 发现零值: " + name + "=" + decoded);
                                decoded = "";
                                System.out.println("W: 替换零值: " + name + "=" + decoded);
                            }
                        } else {
                            Log.w(TAG, "Unexpected attribute name: " + nameEntry);
                        }
                    } catch (UndefinedResObjectException ignored) {
                    }
                } else {
                    // Format the value with the default attribute.
                    decoded = ResAttribute.DEFAULT.formatAsAttributeValue(value);
                }
            }
        } catch (AndrolibException ex) {
            if (mFirstError == null) {
                mFirstError = ex;
            }
        }

        if (decoded == null) {
            if (name == null) {
                name = mStringPool != null ? mStringPool.getString(attr.name) : null;
            }

            Log.w(TAG, "Could not decode attribute value: ns=%s, name=%s, type=0x%02x, value=0x%08x",
                getAttributePrefix(index), name, attr.valueType, attr.valueData);

            if (value != null) {
                // Format the value with the default attribute.
                try {
                    decoded = ResAttribute.DEFAULT.formatAsAttributeValue(value);
                } catch (AndrolibException ignored) {
                }
            }
            if (decoded == null) {
                decoded = "";
            }
        }

        return decoded;
    }