Android App Bundle introduces Resource Not found crash in Android app

JavaAndroidCrashAndroid App-Bundle

Java Problem Overview


By using Android's new Android App Bundle, I have received a Resource Not Found error in 2 of my Google Play Store apps.

Here is the stacktrace from fabric for one of the apps:

Unable to start activity ComponentInfo{/com.Lastyear.MainActivity}: android.content.res.Resources$NotFoundException: File res/drawable/abc_item_background_holo_dark.xml from drawable resource ID #0x7f08002c
       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2377)
       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2429)
       at android.app.ActivityThread.access$800(ActivityThread.java:151)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1342)
       at android.os.Handler.dispatchMessage(Handler.java:110)
       at android.os.Looper.loop(Looper.java:193)
       at android.app.ActivityThread.main(ActivityThread.java:5363)
       at java.lang.reflect.Method.invokeNative(Method.java)
       at java.lang.reflect.Method.invoke(Method.java:515)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:828)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:644)
       at dalvik.system.NativeStart.main(NativeStart.java)

build.gradle dependencies:

 dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
testImplementation 'junit:junit:4.12'
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support:customtabs:27.1.1'
implementation 'com.android.support:cardview-v7:27.1.1'
implementation 'com.squareup.picasso:picasso:2.5.2'

implementation 'com.android.support:palette-v7:27.1.1'
implementation 'com.afollestad.material-dialogs:core:0.9.6.0'
implementation 'com.jakewharton:butterknife:8.8.1'
implementation 'com.github.bumptech.glide:glide:3.7.0'
implementation 'com.android.support:design:27.1.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
implementation 'com.github.hotchemi:android-rate:1.0.1'
implementation 'com.hannesdorfmann.smoothprogressbar:library:1.0.0'
implementation 'com.android.support:palette-v7:27.1.1'
implementation 'com.google.android.gms:play-services-ads:15.0.1'
implementation 'com.muddzdev:styleabletoast:1.0.9'
implementation 'com.github.GrenderG:Toasty:1.2.5'
implementation 'com.hannesdorfmann.smoothprogressbar:library:1.0.0'

implementation 'com.wang.avi:library:2.1.3'
implementation 'com.github.medyo:fancybuttons:1.8.4'
implementation 'com.irozon.sneaker:sneaker:1.0.1'
implementation 'com.sdsmdg.tastytoast:tastytoast:0.1.1'
implementation 'de.hdodenhof:circleimageview:2.2.0'
implementation 'com.github.barteksc:android-pdf-viewer:2.8.2'


implementation 'com.getkeepsafe.taptargetview:taptargetview:1.11.0'


implementation('com.crashlytics.sdk.android:crashlytics:2.6.8@aar') {
    transitive = true;
}


implementation 'petrov.kristiyan:colorpicker-library:1.1.8'}

One more thing, it is happening only on Android 4 operating system, not on newer versions of Android. I have found that other apps are having the same problem of Resource Not Found, which was not existing before using Android's app bundle. Is there some problem in the library or code or it is because of the beta version of Android's app bundle?

I have also found the resource drawable due to which it crashes:- Screenshot showing the highlighted drawable resource that causes the problem.

I think this question is also related to this one: https://stackoverflow.com/questions/51228226

Java Solutions


Solution 1 - Java

This is almost certainly users sharing (sideloading) the app, either via P2P sharing programs, or uploading the APK to the web then other users downloading and installing from the web.

People used to dealing with non Android App Bundle apps just transfer and share the main APK. But your App bundle app has lots of "split APKs" for things like the resources, that is how the size saving happens. You can read all about this process on the help page. If a user installs the main APK without installing the right split APKs, then a "Resources Not found" crash will occur the first time the app tries to load a resource.

If you want to support users sideloading your app and just the main APK you could try to detect this situation and display a message to the user (without using any resources) that says "Please install from Google Play". Or you could just decide you aren't going to support users who share APKs in this way.

I suspect in the long run the websites and P2P sharing programs will get better at sharing such APKs properly, so I wouldn't spend too long worrying about it.

If you see this happening far more frequently on lower Android versions, this isn't probably due to a bug in lower Android versions. Instead, it is probably because in countries where users commonly P2P share apps (eg India) users also are far more likely to be on older version phones.

Solution 2 - Java

This is a little late but Google has introduced new API for Sideloading crash prevention, which allows you to detect incomplete installation of apps that are built using an Android App Bundle.

> For example, consider an app that uses Android App Bundles to optimize > app download size using split APKs. When a user downloads the app from > the Google Play store, it ensures that the device downloads and > installs the complete set of split APKs required to run that app on > that particular device. When you bypass Google Play to sideload an > app, the platform does not have sufficient data to validate the app > install, and proper functionality of the app is not guaranteed.

First off include the Play Core library 1.6.0 or higher in your project.

Include the following in your app project’s build.gradle file:

buildscript {
    dependencies {
        ...
        // Use bundletool 0.9.0 or higher when building with the
        // Android Gradle plugin.
        classpath 'com.android.tools.build:bundletool:0.9.0'
    }
}

You can use 1 of those 3 below methods

> 1) Register checks through the manifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapplication" >
    <application
        ...
        android:name="com.google.android.play.core.missingsplits.MissingSplitsDetectingApplication" >
    </application>
    ...
</manifest>

> 2) Apply checks in a custom Application class

public class MyCustomApplication extends Application {
    @Override
    public void onCreate() {

        if (MissingSplitsManagerFactory.create(this).disableAppIfMissingRequiredSplits()) {
            // Skip app initialization.
            return;
        }

        super.onCreate();
        ...
    }
}

> 3) Apply checks to content providers

public class ExampleProvider extends ContentProvider {
    @Override
    public boolean onCreate() {

        if (MissingSplitsManagerFactory.create(getContext()).isMissingRequiredSplits()) {
            // Skip provider initialization.
            return false;
        }

        super.onCreate();
        ...
    }
}

Read More : https://developer.android.com/reference/com/google/android/play/core/release-notes?hl=en-419#1-6-0

Solution 3 - Java

The accepted answer is absolutely correct - root of this issue is sideloading of APK file.

Nevertheless, lot of people are still looking for workaround, asking how to correctly handle this case.

In my app I did the following:

  1. Create 1x1 image named pixel.png and put it to all of the following folders: drawable-mdpi, drawable-hdpi, drawable-xhdpi, drawable-xxhdpi, drawable-xxxhdpi.

  2. Create simple Activity which shows static message, e.g. > This copy of app is corrupted and can't be launched. > > Please, install original version from Google Play

  3. Then simply call getDrawable(R.drawable.pixel) from Activity.onCreate() wrapped in try/catch clause.

  4. If the exception was caught, just finish current Activity and start another one from step #2.

Done!

Screenshot

This solution works well, now I even have data from Firebase Analytics confirming this.

From the 46k of new users (event first_open) 266 users got this error (which was caught) and 221 users clicked button which leads to Google Play.

Here is my source code (also available on GitHub):

DrawablesValidator.java

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Space;
import android.widget.TextView;
import android.widget.Toast;

public class DrawablesValidator extends Activity {
    public static void ensureDrawablesValid(@NonNull Activity activity) {
        try {
            // IMPORTANT create 1x1 image named pixel.png and put it to all folders
            //           drawable-mdpi
            //           drawable-hdpi
            //           drawable-xhdpi
            //           drawable-xxhdpi
            //           drawable-xxxhdpi
            activity.getDrawable(R.drawable.pixel);
        } catch (Resources.NotFoundException ex) {
            // NOTE optionally, report exception to Crashlytics or just an event to Analytics

            activity.finish();
            activity.startActivity(new Intent(activity, DrawablesValidator.class));
        }
    }

    // NOTE don't care about translations of text messages here, don't put them to strings.xml
    //      we assume, that if user is smart enough to get APK from outside and install it,
    //      then user will definitely understand few messages in English :)
    @SuppressLint("SetTextI18n")
    @Override
    protected void onCreate(Bundle state) {
        super.onCreate(state);

        int dp = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());
        int dp8 = dp * 8;
        int dp16 = dp * 16;
        int dp80 = dp * 80;

        LinearLayout root = new LinearLayout(this);
        root.setOrientation(LinearLayout.VERTICAL);
        root.setGravity(Gravity.CENTER_HORIZONTAL);
        root.setPadding(dp80, dp16, dp80, dp16);

        Space spaceTop = new Space(this);

        TextView title = new TextView(this);
        title.setPadding(0, dp8, 0, dp8);
        title.setTextSize(20);
        title.setText("Re-install app");

        TextView message = new TextView(this);
        message.setPadding(0, dp8, 0, dp8);
        message.setTextSize(16);
        message.setText("This copy of app is corrupted and can't be launched." +
                "\n\n" +
                "Please, install original version from Google Play");

        Button button = new Button(this);
        button.setPadding(dp16, dp8, dp16, dp8);
        button.setText("Continue");
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=" + getPackageName())));
                } catch (Exception ex) {
                    Toast.makeText(getApplicationContext(), "Can't open Google Play", Toast.LENGTH_SHORT).show();
                }
            }
        });

        Space spaceBottom = new Space(this);

        int wc = ViewGroup.LayoutParams.WRAP_CONTENT;
        int mp = ViewGroup.LayoutParams.MATCH_PARENT;

        root.addView(spaceTop, lp(0, 0, 1, -1));
        root.addView(title, lp(wc, wc, 0, -1));
        root.addView(message, lp(mp, wc, 0, -1));
        root.addView(button, lp(wc, wc, 0, Gravity.END));
        root.addView(spaceBottom, lp(mp, wc, 1, -1));

        setContentView(root);
    }

    private LinearLayout.LayoutParams lp(int width, int height, int weight, int gravity) {
        LinearLayout.LayoutParams result = new LinearLayout.LayoutParams(width, height);
        result.weight = weight;
        result.gravity = gravity;
        return result;
    }
}

Solution 4 - Java

The issue is likely to be that your app has been sideloaded, i.e. not installed via the Play Store, and incompatible APKs have been manually installed on those devices.

Solution 5 - Java

As this is happening only on Android 4 devices after migrating to Android App Bundle, i found out a way of this after adding:-

public class App extends Application {

static {
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); }

And in build.gradle:-

android {
  defaultConfig {
    vectorDrawables.useSupportLibrary = true
  }
}

as explained in this post:- https://stackoverflow.com/questions/36867298/using-android-vector-drawables-on-pre-lollipop-crash

Regarding the second question :- https://stackoverflow.com/questions/51228226/resource-not-found-error-res-drawable-abc-switch-thumb-material-xml-after-adding?noredirect=1&lq=1

As this is happening on all Android versions. I Sideloaded the Apk and able to reproduce the same error in logcat , So this can only be fixed by removing the SwitchCompat from my project , i know its a temporary fix and Google should surely do something about it so that at least crash does not happen after sideloading the apk, maybe redirect to play store would be the better option. But crashing of the app after migrating to Android App Bundle is definitely affects the stability of the app as many users do it on regular basis.

Solution 6 - Java

You can check users sharing (sideloading) the app from play console Login play console select your app on which you publish app bundle instead of apk.

Select Android Vital -> ANRs & crashes and click on crashes tab.

select install from play

enter image description here

Solution 7 - Java

For Internal testing we cant side load the aab file apks. There only two ways give apk for user before releasing to production.

  1. we have to give universal.apk file checking purpose

java -jar bundletool-all-0.12.0.jar build-apks --bundle=(build path)debug.aab --output=(output path)debug.apks --mode=universal

  1. we have to upload aab file to playstore then first release for internal testing if every thing works fine then release it production.

Solution 8 - Java

I faced the same issue my app was getting crashes where it was unable to locate fonts and audio files from resources. the issue was that I updated the Gradle URL from 4.10.1 to 5.6.4 in Gradle-wrapper.properties but didn't update the libraries. when I reverted back to 4.10.1 it started working normally.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiongautamView Question on Stackoverflow
Solution 1 - JavaNick FortescueView Answer on Stackoverflow
Solution 2 - JavaEAK TEAMView Answer on Stackoverflow
Solution 3 - JavaOleksii K.View Answer on Stackoverflow
Solution 4 - JavaPierreView Answer on Stackoverflow
Solution 5 - JavagautamView Answer on Stackoverflow
Solution 6 - JavaAshish SinghView Answer on Stackoverflow
Solution 7 - JavaYugandhar VadlamudiView Answer on Stackoverflow
Solution 8 - JavaAli MehdiView Answer on Stackoverflow