Binary XML file line #17 tag requires viewportWidth >0

前言

       近期把項目中的support包升級到了26.1.0,看起來在手機上運行的穩穩的,沒翻船!之後提交,測試就報了一個bug說5.0以下的手機都不能運行了!因此就排查了問題原因。

問題

       首先我們復現一下問題,看看崩潰日誌,崩潰日誌主要有以下兩個:

E/VdcInflateDelegate: Exception while inflating <vector>
org.xmlpull.v1.XmlPullParserException: Binary XML file line #17<vector> tag requires viewportWidth > 0
  at android.support.graphics.drawable.VectorDrawableCompat.updateStateFromTypedArray(VectorDrawableCompat.java:690)
  at android.support.graphics.drawable.VectorDrawableCompat.inflate(VectorDrawableCompat.java:623)
  at android.support.graphics.drawable.VectorDrawableCompat.createFromXmlInner(VectorDrawableCompat.java:586)
  at android.support.v7.widget.AppCompatDrawableManager$VdcInflateDelegate.createFromXmlInner(AppCompatDrawableManager.java:777)
  at android.support.v7.widget.AppCompatDrawableManager.loadDrawableFromDelegates(AppCompatDrawableManager.java:365)
  at android.support.v7.widget.AppCompatDrawableManager.getDrawable(AppCompatDrawableManager.java:195)
  at android.support.v7.widget.AppCompatDrawableManager.getDrawable(AppCompatDrawableManager.java:188)
  at android.support.v7.widget.AppCompatDrawableManager.checkVectorDrawableSetup(AppCompatDrawableManager.java:755)
  at android.support.v7.widget.AppCompatDrawableManager.getDrawable(AppCompatDrawableManager.java:193)
  at android.support.v7.widget.TintTypedArray.getDrawableIfKnown(TintTypedArray.java:87)
  at android.support.v7.app.AppCompatDelegateImplBase.<init>(AppCompatDelegateImplBase.java:128)
  at android.support.v7.app.AppCompatDelegateImplV9.<init>(AppCompatDelegateImplV9.java:149)
  at android.support.v7.app.AppCompatDelegateImplV11.<init>(AppCompatDelegateImplV11.java:29)
  at android.support.v7.app.AppCompatDelegateImplV14.<init>(AppCompatDelegateImplV14.java:54)
  at android.support.v7.app.AppCompatDelegate.create(AppCompatDelegate.java:202)
  at android.support.v7.app.AppCompatDelegate.create(AppCompatDelegate.java:183)
  at android.support.v7.app.AppCompatActivity.getDelegate(AppCompatActivity.java:519)
  at com.xxx.common.activity.BaseActionBarActivity.installInflaterFactory(BaseActionBarActivity.java:192)
  at com.xxx.common.activity.BaseActionBarActivity.onCreate(BaseActionBarActivity.java:156)
  at com.xxx.activity.login.EntranceActivity.onCreate(EntranceActivity.java:64)
  at android.app.Activity.performCreate(Activity.java:5356)
  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1089)
  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2151)
  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2236)
  at android.app.ActivityThread.access$800(ActivityThread.java:138)
  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1199)
  at android.os.Handler.dispatchMessage(Handler.java:102)
  at android.os.Looper.loop(Looper.java:136)
  at android.app.ActivityThread.main(ActivityThread.java:5122)
  at java.lang.reflect.Method.invokeNative(Native Method)
  at java.lang.reflect.Method.invoke(Method.java:515)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
  at dalvik.system.NativeStart.main(Native Method)   

       以及如下的錯誤,如下的錯誤會導致系統崩潰不能運行。

E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.xxx.debug, PID: 3565
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.xxx.debug/com.xxx.activity.login.EntranceActivity}: android.content.res.Resources$NotFoundException: File res/drawable/abc_vector_test.xml from drawable resource ID #0x7f020052
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2187)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2236)
at android.app.ActivityThread.access$800(ActivityThread.java:138)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1199)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5122)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
at dalvik.system.NativeStart.main(Native Method)
Caused by: android.content.res.Resources$NotFoundException: File res/drawable/abc_vector_test.xml from drawable resource ID #0x7f020052
at android.content.res.Resources.loadDrawable(Resources.java:2124)
at android.content.res.Resources.getDrawable(Resources.java:704)
at android.support.v7.widget.VectorEnabledTintResources.superGetDrawable(VectorEnabledTintResources.java:74)
at android.support.v7.widget.AppCompatDrawableManager.onDrawableLoadedFromResources(AppCompatDrawableManager.java:435)
at android.support.v7.widget.VectorEnabledTintResources.getDrawable(VectorEnabledTintResources.java:67)
at android.support.v4.content.ContextCompat.getDrawable(ContextCompat.java:353)
at android.support.v7.widget.AppCompatDrawableManager.getDrawable(AppCompatDrawableManager.java:200)
at android.support.v7.widget.AppCompatDrawableManager.getDrawable(AppCompatDrawableManager.java:188)
at android.support.v7.widget.AppCompatDrawableManager.checkVectorDrawableSetup(AppCompatDrawableManager.java:755)
at android.support.v7.widget.AppCompatDrawableManager.getDrawable(AppCompatDrawableManager.java:193)
at android.support.v7.widget.TintTypedArray.getDrawableIfKnown(TintTypedArray.java:87)
at android.support.v7.app.AppCompatDelegateImplBase.<init>(AppCompatDelegateImplBase.java:128)
at android.support.v7.app.AppCompatDelegateImplV9.<init>(AppCompatDelegateImplV9.java:149)
at android.support.v7.app.AppCompatDelegateImplV11.<init>(AppCompatDelegateImplV11.java:29)
at android.support.v7.app.AppCompatDelegateImplV14.<init>(AppCompatDelegateImplV14.java:54)
at android.support.v7.app.AppCompatDelegate.create(AppCompatDelegate.java:202)
at android.support.v7.app.AppCompatDelegate.create(AppCompatDelegate.java:183)
at android.support.v7.app.AppCompatActivity.getDelegate(AppCompatActivity.java:519)
at com.xxx.common.activity.BaseActionBarActivity.installInflaterFactory(BaseActionBarActivity.java:192)
at com.xxx.common.activity.BaseActionBarActivity.onCreate(BaseActionBarActivity.java:156)
at com.xxx.activity.login.EntranceActivity.onCreate(EntranceActivity.java:64)
at android.app.Activity.performCreate(Activity.java:5356)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1089)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2151)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2236) 
at android.app.ActivityThread.access$800(ActivityThread.java:138) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1199) 
at android.os.Handler.dispatchMessage(Handler.java:102) 
at android.os.Looper.loop(Looper.java:136) 
at android.app.ActivityThread.main(ActivityThread.java:5122) 
at java.lang.reflect.Method.invokeNative(Native Method) 
at java.lang.reflect.Method.invoke(Method.java:515) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602) 
at dalvik.system.NativeStart.main(Native Method) 
Caused by: org.xmlpull.v1.XmlPullParserException: Binary XML file line #17: invalid drawable tag vector
at android.graphics.drawable.Drawable.createFromXmlInner(Drawable.java:933)
at android.graphics.drawable.Drawable.createFromXml(Drawable.java:877)
at android.content.res.Resources.loadDrawable(Resources.java:2120)
at android.content.res.Resources.getDrawable(Resources.java:704) 
at android.support.v7.widget.VectorEnabledTintResources.superGetDrawable(VectorEnabledTintResources.java:74) 
at android.support.v7.widget.AppCompatDrawableManager.onDrawableLoadedFromResources(AppCompatDrawableManager.java:435) 
at android.support.v7.widget.VectorEnabledTintResources.getDrawable(VectorEnabledTintResources.java:67) 
at android.support.v4.content.ContextCompat.getDrawable(ContextCompat.java:353) 
at android.support.v7.widget.AppCompatDrawableManager.getDrawable(AppCompatDrawableManager.java:200) 
at android.support.v7.widget.AppCompatDrawableManager.getDrawable(AppCompatDrawableManager.java:188) 
at android.support.v7.widget.AppCompatDrawableManager.checkVectorDrawableSetup(AppCompatDrawableManager.java:755) 
at android.support.v7.widget.AppCompatDrawableManager.getDrawable(AppCompatDrawableManager.java:193) 
at android.support.v7.widget.TintTypedArray.getDrawableIfKnown(TintTypedArray.java:87) 
at android.support.v7.app.AppCompatDelegateImplBase.<init>(AppCompatDelegateImplBase.java:128) 
at android.support.v7.app.AppCompatDelegateImplV9.<init>(AppCompatDelegateImplV9.java:149) 
at android.support.v7.app.AppCompatDelegateImplV11.<init>(AppCompatDelegateImplV11.java:29) 
at android.support.v7.app.AppCompatDelegateImplV14.<init>(AppCompatDelegateImplV14.java:54) 
at android.support.v7.app.AppCompatDelegate.create(AppCompatDelegate.java:202) 
at android.support.v7.app.AppCompatDelegate.create(AppCompatDelegate.java:183) 
at android.support.v7.app.AppCompatActivity.getDelegate(AppCompatActivity.java:519) 
at com.xxx.common.activity.BaseActionBarActivity.installInflaterFactory(BaseActionBarActivity.java:192) 
at com.xxx.common.activity.BaseActionBarActivity.onCreate(BaseActionBarActivity.java:156) 
at com.xxx.activity.login.EntranceActivity.onCreate(EntranceActivity.java:64) 
at android.app.Activity.performCreate(Activity.java:5356) 
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1089) 
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2151) 
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2236) 
at android.app.ActivityThread.access$800(ActivityThread.java:138) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1199) 
at android.os.Handler.dispatchMessage(Handler.java:102) 
at android.os.Looper.loop(Looper.java:136) 
at android.app.ActivityThread.main(ActivityThread.java:5122) 
at java.lang.reflect.Method.invokeNative(Native Method) 
at java.lang.reflect.Method.invoke(Method.java:515) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602) 
at dalvik.system.NativeStart.main(Native Method) 

原因

       由於歷史原因,以前做了很多分包的操作,因此gradle版本一直是1.2.3,不是當前最新的版本,因此寫了一個Demo發現採用最新的版本不會出現崩潰。因此根據代碼我們可以看到有如下執行路徑:

日誌1

       Activity繼承自AppCompatActivity,在Activity加載的時候會調用到getDelegate()函數:

@NonNull
public AppCompatDelegate getDelegate() {
    if (mDelegate == null) {
        mDelegate = AppCompatDelegate.create(this, this);
    }
    return mDelegate;
}

       最終會調用到create函數:

private static AppCompatDelegate create(Context context, Window window,
        AppCompatCallback callback) {
    if (Build.VERSION.SDK_INT >= 24) {
        return new AppCompatDelegateImplN(context, window, callback);
    } else if (Build.VERSION.SDK_INT >= 23) {
        return new AppCompatDelegateImplV23(context, window, callback);
    } else if (Build.VERSION.SDK_INT >= 14) {
        return new AppCompatDelegateImplV14(context, window, callback);
    } else if (Build.VERSION.SDK_INT >= 11) {
        return new AppCompatDelegateImplV11(context, window, callback);
    } else {
        return new AppCompatDelegateImplV9(context, window, callback);
    }
}

       這裏我們主要是5.0以下版本會崩潰,因此我們需要查看AppCompatDelegateImplV14,裏面我們一直跟蹤調用父類,最終會調用到如下的代碼:

final TintTypedArray a = TintTypedArray.obtainStyledAttributes(
context, null, sWindowBackgroundStyleable);
final Drawable winBg = a.getDrawableIfKnown(0);

       這裏先解析是否有windowbackground屬性,並且獲取對應的drawable:

public Drawable getDrawableIfKnown(int index) {
    if (mWrapped.hasValue(index)) {
        final int resourceId = mWrapped.getResourceId(index, 0);
        if (resourceId != 0) {
            return AppCompatDrawableManager.get().getDrawable(mContext, resourceId, true);
        }
    }
    return null;
}

       這裏recouseId不等於0,進入到AppCompatDrawableManager.中的getDrawable函數:

Drawable getDrawable(@NonNull Context context, @DrawableRes int resId,
        boolean failIfNotKnown) {
    checkVectorDrawableSetup(context);

    Drawable drawable = loadDrawableFromDelegates(context, resId);
    if (drawable == null) {
        drawable = createDrawableIfNeeded(context, resId);
    }
    if (drawable == null) {
        drawable = ContextCompat.getDrawable(context, resId);
    }

    if (drawable != null) {
        // Tint it if needed
        drawable = tintDrawable(context, resId, failIfNotKnown, drawable);
    }
    if (drawable != null) {
        // See if we need to 'fix' the drawable
        DrawableUtils.fixDrawable(drawable);
    }
    return drawable;
}

       getDrawable函數中首先調用了checkVectorDrawableSetup(context)函數:

private void checkVectorDrawableSetup(@NonNull Context context) {
    if (mHasCheckedVectorDrawableSetup) {
        // We've already checked so return now...
        return;
    }
    // Here we will check that a known Vector drawable resource inside AppCompat can be
    // correctly decoded
    mHasCheckedVectorDrawableSetup = true;
    final Drawable d = getDrawable(context, R.drawable.abc_vector_test);
    if (d == null || !isVectorDrawable(d)) {
        mHasCheckedVectorDrawableSetup = false;
        throw new IllegalStateException("This app has been built with an incorrect "
                + "configuration. Please configure your build for VectorDrawableCompat.");
    }
}

       我們發現上面有一個資源R.drawable.abc_vector_test,這個系統support包裏的Vector資源,我們先來看看他的xml:


<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="24dp"
        android:height="24dp"
        android:viewportHeight="24.0"
        android:viewportWidth="24.0">
    <path
        android:fillColor="@android:color/white"
        android:pathData="M20,11L7.8,11l5.6,-5.6L12,4l-8,8l8,8l1.4,-1.4L7.8,13L20,13L20,11z"/>
</vector>

       我們繼續往下看,這裏初始狀態爲false,因此會繼續往下執行,首先改爲了true,並且繼續調用了上面的getDrawable函數,因爲這裏改爲了true,因此會進入的到getDrawable函數中的loadDrawableFromDelegates:


private Drawable loadDrawableFromDelegates(@NonNull Context context, @DrawableRes int resId) {
    if (mDelegates != null && !mDelegates.isEmpty()) {
         ............................
        if (tv.string != null && tv.string.toString().endsWith(".xml")) {
            // If the resource is an XML file, let's try and parse it
            try {
                final XmlPullParser parser = res.getXml(resId);
                final AttributeSet attrs = Xml.asAttributeSet(parser);
                int type;
                while ((type = parser.next()) != XmlPullParser.START_TAG &&
                        type != XmlPullParser.END_DOCUMENT) {
                    // Empty loop
                }
                if (type != XmlPullParser.START_TAG) {
                    throw new XmlPullParserException("No start tag found");
                }

                final String tagName = parser.getName();
                // Add the tag name to the cache
                mKnownDrawableIdTags.append(resId, tagName);

                // Now try and find a delegate for the tag name and inflate if found
                final InflateDelegate delegate = mDelegates.get(tagName);
                if (delegate != null) {
                    dr = delegate.createFromXmlInner(context, parser, attrs,
                            context.getTheme());
                }
                if (dr != null) {
                    // Add it to the drawable cache
                    dr.setChangingConfigurations(tv.changingConfigurations);
                    if (addDrawableToCache(context, key, dr) && DEBUG) {
                        Log.i(TAG, "[loadDrawableFromDelegates] Saved drawable to cache: " +
                                context.getResources().getResourceName(resId));
                    }
                }
            } catch (Exception e) {
                Log.e(TAG, "Exception while inflating drawable", e);
            }
        }
    .............
        return dr;
    }

    return null;
}

       這裏我們先刪除了部分代碼,只看最重要的部分,那就是delegate.createFromXmlInner,實際調用的是VectorDrawableCompat
.createFromXmlInner(context.getResources(), parser, attrs, theme),繼續調用createFromXmlInner中的inflate函數,inflate中繼續調用到updateStateFromTypedArray:

private void updateStateFromTypedArray(TypedArray a, XmlPullParser parser)
        throws XmlPullParserException {
    final VectorDrawableCompatState state = mVectorState;
    final VPathRenderer pathRenderer = state.mVPathRenderer;

    // Account for any configuration changes.
    // state.mChangingConfigurations |= Utils.getChangingConfigurations(a);

    final int mode = TypedArrayUtils.getNamedInt(a, parser, "tintMode",
            AndroidResources.STYLEABLE_VECTOR_DRAWABLE_TINT_MODE, -1);
    state.mTintMode = parseTintModeCompat(mode, Mode.SRC_IN);

    final ColorStateList tint =
            a.getColorStateList(AndroidResources.STYLEABLE_VECTOR_DRAWABLE_TINT);
    if (tint != null) {
        state.mTint = tint;
    }

    state.mAutoMirrored = TypedArrayUtils.getNamedBoolean(a, parser, "autoMirrored",
            AndroidResources.STYLEABLE_VECTOR_DRAWABLE_AUTO_MIRRORED, state.mAutoMirrored);

    pathRenderer.mViewportWidth = TypedArrayUtils.getNamedFloat(a, parser, "viewportWidth",
            AndroidResources.STYLEABLE_VECTOR_DRAWABLE_VIEWPORT_WIDTH,
            pathRenderer.mViewportWidth);

    pathRenderer.mViewportHeight = TypedArrayUtils.getNamedFloat(a, parser, "viewportHeight",
            AndroidResources.STYLEABLE_VECTOR_DRAWABLE_VIEWPORT_HEIGHT,
            pathRenderer.mViewportHeight);

    if (pathRenderer.mViewportWidth <= 0) {
        throw new XmlPullParserException(a.getPositionDescription() +
                "<vector> tag requires viewportWidth > 0");
    } else if (pathRenderer.mViewportHeight <= 0) {
        throw new XmlPullParserException(a.getPositionDescription() +
                "<vector> tag requires viewportHeight > 0");
    }

    pathRenderer.mBaseWidth = a.getDimension(
            AndroidResources.STYLEABLE_VECTOR_DRAWABLE_WIDTH, pathRenderer.mBaseWidth);
    pathRenderer.mBaseHeight = a.getDimension(
            AndroidResources.STYLEABLE_VECTOR_DRAWABLE_HEIGHT, pathRenderer.mBaseHeight);
    if (pathRenderer.mBaseWidth <= 0) {
        throw new XmlPullParserException(a.getPositionDescription() +
                "<vector> tag requires width > 0");
    } else if (pathRenderer.mBaseHeight <= 0) {
        throw new XmlPullParserException(a.getPositionDescription() +
                "<vector> tag requires height > 0");
    }

    // shown up from API 11.
    final float alphaInFloat = TypedArrayUtils.getNamedFloat(a, parser, "alpha",
            AndroidResources.STYLEABLE_VECTOR_DRAWABLE_ALPHA, pathRenderer.getAlpha());
    pathRenderer.setAlpha(alphaInFloat);

    final String name = a.getString(AndroidResources.STYLEABLE_VECTOR_DRAWABLE_NAME);
    if (name != null) {
        pathRenderer.mRootName = name;
        pathRenderer.mVGTargetsMap.put(name, pathRenderer);
    }
}

       這裏調用TypedArrayUtils.getNamedFloat會去解析viewportWidth與viewportHeight:

public static float getNamedFloat(@NonNull TypedArray a, @NonNull XmlPullParser parser,
        @NonNull String attrName, @StyleableRes int resId, float defaultValue) {
    final boolean hasAttr = hasAttribute(parser, attrName);
    if (!hasAttr) {
        return defaultValue;
    } else {
        return a.getFloat(resId, defaultValue);
    }
}

   public static boolean hasAttribute(@NonNull XmlPullParser parser, @NonNull String attrName) {
    return parser.getAttributeValue(NAMESPACE, attrName) != null;
}

       這裏會發現viewportHeight與viewportWidth不存在,但是我們上面的xml裏明明有這兩個屬性,爲什麼到這個地方就沒有了?

gradle 1.2.3調用aapt編譯資源,因爲VectorDrawable是21才加入的,因此他不認識這兩個屬性,被優化掉了,同時優化掉的還有path中的內容

       由於上面的原因,xml在編譯後viewportWidth與viewportHeight會移除了,因此在解析的時候返回了0,系統檢測該值,拋出了異常!

  • 爲什麼23的版本不崩潰

23版本的內容未做檢查,不會調用checkVectorDrawableSetup函數

日誌2

       上面有兩個日誌,第一個日誌我們已經清楚了,只是拋出了異常,而不會導致崩潰,那爲什麼系統會崩潰,我們看到上面日誌的下半部分與日誌1相同,所以原因也是一樣的,我們繼續往下看loadDrawableFromDelegates解析失敗後drawable爲空,會繼續執行createDrawableIfNeeded,這裏只是對想要資源做了檢查,因此最終會調用到ContextCompat.getDrawable中:

public static final Drawable getDrawable(Context context, @DrawableRes int id) {
    if (Build.VERSION.SDK_INT >= 21) {
        return context.getDrawable(id);
    } else if (Build.VERSION.SDK_INT >= 16) {
        return context.getResources().getDrawable(id);
    } else {
        // Prior to JELLY_BEAN, Resources.getDrawable() would not correctly
        // retrieve the final configuration density when the resource ID
        // is a reference another Drawable resource. As a workaround, try
        // to resolve the drawable reference manually.
        final int resolvedId;
        synchronized (sLock) {
            if (sTempValue == null) {
                sTempValue = new TypedValue();
            }
            context.getResources().getValue(id, sTempValue, true);
            resolvedId = sTempValue.resourceId;
        }
        return context.getResources().getDrawable(resolvedId);
    }
}

       這裏我們調用到context.getResources().getDrawable(id), 而5.0以上的版本會調用 context.getDrawable(id),這裏的Resources是什麼?我們在AppCompatActivity中有一個如下函數:

@Override
public Resources getResources() {
    if (mResources == null && VectorEnabledTintResources.shouldBeUsed()) {
        mResources = new VectorEnabledTintResources(this, super.getResources());
    }
    return mResources == null ? super.getResources() : mResources;
}

       如果這裏VectorEnabledTintResources.shouldBeUsed是true因此會進入到VectorEnabledTintResources, 如果爲false調用系統,5.0以下的系統是不能解析Vector的因此這裏我們需要讓上面的條件爲true,shouldBeUsed的代碼如下 :

public static boolean shouldBeUsed() {
    return AppCompatDelegate.isCompatVectorFromResourcesEnabled()
            && Build.VERSION.SDK_INT <= MAX_SDK_WHERE_REQUIRED;
}

       這裏條件2已經滿足了,因此我們需要滿足條件1,那條件1怎麼滿足,AppCompatDelegate有一個靜態函數,我們可以再系統初始化的時候進行調用:

AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)

       如果這裏條件不滿足,系統rescoures不認識該資源,系統崩潰,如果這裏條件滿足,那就會調用VectorEnabledTintResources的getDrawable函數:

@Override
public Drawable getDrawable(int id) throws NotFoundException {
    final Context context = mContextRef.get();
    if (context != null) {
        return AppCompatDrawableManager.get().onDrawableLoadedFromResources(context, this, id);
    } else {
        return super.getDrawable(id);

       這裏context不爲空,如果爲空調用下面崩潰。這裏繼續調用AppCompatDrawableManager的onDrawableLoadedFromResources函數:

Drawable onDrawableLoadedFromResources(@NonNull Context context,
        @NonNull VectorEnabledTintResources resources, @DrawableRes final int resId) {
    Drawable drawable = loadDrawableFromDelegates(context, resId);
    if (drawable == null) {
        drawable = resources.superGetDrawable(resId);
    }
    if (drawable != null) {
        return tintDrawable(context, resId, false, drawable);
    }
    return null;
}

       這裏我們看到了一個熟悉的函數,loadDrawableFromDelegates,這裏下面就跟日誌1一樣,因爲viewportWidth與viewportHeight不存在,導致出現日誌2下班部分的異常,這裏返回drawable爲空,繼續調用resources.superGetDrawable,進入系統resources調用,由於5.0以下版本解析不了vector TAG,具體調用代碼如下:

public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs)
throws XmlPullParserException, IOException {
   Drawable drawable;

   final String name = parser.getName();

   if (name.equals("selector")) {
       drawable = new StateListDrawable();
   } else if (name.equals("level-list")) {
       drawable = new LevelListDrawable();
   } else if (name.equals("layer-list")) {
       drawable = new LayerDrawable();
   } else if (name.equals("transition")) {
       drawable = new TransitionDrawable();
   } else if (name.equals("color")) {
       drawable = new ColorDrawable();
   } else if (name.equals("shape")) {
       drawable = new GradientDrawable();
   } else if (name.equals("scale")) {
       drawable = new ScaleDrawable();
   } else if (name.equals("clip")) {
       drawable = new ClipDrawable();
   } else if (name.equals("rotate")) {
       drawable = new RotateDrawable();
   } else if (name.equals("animated-rotate")) {
       drawable = new AnimatedRotateDrawable();
   } else if (name.equals("animation-list")) {
       drawable = new AnimationDrawable();
   } else if (name.equals("inset")) {
       drawable = new InsetDrawable();
   } else if (name.equals("bitmap")) {
       //noinspection deprecation
       drawable = new BitmapDrawable(r);
       if (r != null) {
          ((BitmapDrawable) drawable).setTargetDensity(r.getDisplayMetrics());
       }
   } else if (name.equals("nine-patch")) {
       drawable = new NinePatchDrawable();
       if (r != null) {
           ((NinePatchDrawable) drawable).setTargetDensity(r.getDisplayMetrics());
        }
   } else {
       throw new XmlPullParserException(parser.getPositionDescription() +
               ": invalid drawable tag " + name);
   }

   drawable.inflate(r, parser, attrs);
   return drawable;
}

       未解析vector標籤,拋出異常,大於fileNotFound,該異常未被捕獲,因此係統崩潰。

5.0以上版本,雖然viewportWidth與viewportHeight被擦除,但是可以解析vector標籤,因此會生成對應的vectordrawable。

解決方案

       上面的出現的種種問題怎麼解決?主要有以下幾種方案:

方案1

       最正確的解法還是需要升級gradle,將gradle升級最新版本就可以了,只要能大於1.2.3就有解,首先在調用如下代碼:

 AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);

       如果是1.5版本如下:

// Gradle Plugin 1.5  
 android {  
   defaultConfig {  
     generatedDensities = []  
  }  

  // This is handled for you by the 2.0+ Gradle Plugin  
  aaptOptions {  
    additionalParameters "--no-version-vectors"  
  }  
 }

       2.0版本如下:

// Gradle Plugin 2.0+  
 android {  
   defaultConfig {  
     vectorDrawables.useSupportLibrary = true  
    }  
 }

       更新的版本啥都不用做。

方案2

       如果因爲種種原因不能升級gradle怎麼辦?也可以,我們可以用高版本打包,將Vector編譯的資源拷貝出來,在低版本打包的時候進行覆蓋。

該方案工作量太大,每次打包都必須覆蓋,並且需要找到所有的vector資源

方案3

       該方案是一個取巧的方案那就是不升級support包,只要在24以下,都未做檢查,因此不會崩潰。

該方案的前提是自生應用中不能包含Vector資源,如有該方案行不通
如果必須升級support包,該方案也行不通

方案4

       我們知道資源是在aapt中被移除的,怎麼才能不移除,添加方案1中的參數,然而gradle1.2.3不識別該參數,怎麼辦?我們可以採用級聯調用,我們在build-tool中新建aapt,重命名掉系統的aapt,由我們的aapt調用系統的aapt在調用時,添加對應的參數:

#!/bin/sh
#ashap=$*
#echo "input $*"
#echo "xxx-test $*" >> /Users/asha/Library/Android/sdk/build-tools/25.0.2/cmd.txt
/Users/xxxxx/Library/Android/sdk/build-tools/25.0.2/aapt-x $* "--no-version-vectors"
exit $?
#exit 0

       這樣就將對應的參數傳遞給了對應的aapt。

該方案必須要新建appt,讓系統先調用到自己的,再調用到系統

總結

       這次是因爲系統的recyclerview有bug,必須升級所以才引發了這一系列的問題。總之升級gradle是王道。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章