ResourcesCompat.getDrawable() vs AppCompatResources.getDrawable()

Here is my understanding after some testing:

ContextCompat.getDrawable(@NonNull Context context, @DrawableRes int resId)

ResourcesCompat.getDrawable(@NonNull Resources res, @DrawableRes int id, @Nullable Theme theme)

AppCompatResources.getDrawable(@NonNull Context context, @DrawableRes int resId)

VectorDrawableCompat.create(@NonNull Resources res, @DrawableRes int resId, @Nullable Theme theme

The first thing I see is VectorDrawableCompat and ResourcesCompat can specify a theme.

I) Without using

AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); in onCreated of the Application class

1) For vector images

  • API >= 21

  • ContextCompat works well

  • ResourcesCompat works well

  • AppCompatResources works well

  • VectorDrawableCompat works well

  • API < 21

  • ContextCompat crash

  • ResourcesCompat crash

  • AppCompatResources works well

  • VectorDrawableCompat works well

2) For normal image

  • In all API levels
  • ContextCompat works well
  • ResourcesCompat works well
  • AppCompatResources works well
  • VectorDrawableCompat crash

II) Using

AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); in onCreated of the Application class

1) For vector images

  • In all API levels
  • ContextCompat works well
  • ResourcesCompat works well
  • AppCompatResources works well
  • VectorDrawableCompat works well

2) For normal images

  • In all API levels
  • ContextCompat works well
  • ResourcesCompat works well
  • AppCompatResources works well
  • VectorDrawableCompat crash

Looking at the source code of the two methods, they seem very similar. If you don't have vectors, you could probably get away with using either one or the other.

ResourcesCompat.getDrawable() will call Resources#getDrawable(int, theme) on APIs 21 or greater. It also supports Android APIs 4+. It is no more than this:

public Drawable getDrawable(Resources res, int id, Theme theme)
        throws NotFoundException {
    final int version = Build.VERSION.SDK_INT;
    if (version >= 21) {
        return ResourcesCompatApi21.getDrawable(res, id, theme);
    } else {
        return res.getDrawable(id);
    }
}

Where-in ResourcesCompatApi21 merely calls res.getDrawable(id, theme). This means it will not allow vector drawables to be drawn if the device does not support vector drawables. It will, however, allow you to pass in a theme.

Meanwhile, the code change for AppCompatResources.getDrawable(Context context, int resId) eventually lands to this:

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;
}

So this instance it will attempt to draw the resource if it can, otherwise it looks in the ContextCompat version to get the resource. Then it will even tint it if necessary. However, this method only supports API 7+.

So I guess to decide if you should use either,

  1. Do you have to support API 4, 5, or 6?

    • Yes: No choice but to use ResourcesCompat or ContextCompat.
    • No: Keep going to #2.
  2. Do you absolutely need to supply a custom Theme?

    • Yes: No choice but to use ResourcesCompat
    • No: Use AppCompatResources