65

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: Resource Not Found error res/drawable/abc_switch_thumb_material.xml after adding SwitchCompat in Android App Bundle

4

8 回答 8

49

这几乎可以肯定是用户通过 P2P 共享程序共享(旁加载)应用程序,或者将 APK 上传到网络,然后其他用户从网络下载和安装。

习惯于处理非 Android App Bundle 应用程序的人们只是传输和共享主 APK。但是您的 App bundle 应用程序有很多用于资源之类的“拆分 APK”,这就是节省大小的方式。您可以在帮助页面上阅读有关此过程的所有信息。如果用户在安装主 APK 时未安装正确的拆分 APK,则应用第一次尝试加载资源时会发生“找不到资源”崩溃。

如果您想支持用户侧载您的应用程序和主 APK,您可以尝试检测这种情况并向用户显示一条消息(不使用任何资源),上面写着“请从 Google Play 安装”。或者您可以决定不支持以这种方式共享 APK 的用户。

我怀疑从长远来看,网站和 P2P 共享程序会更好地正确共享此类 APK,所以我不会花太长时间担心它。

如果您发现这种情况在较低的 Android 版本上发生得更频繁,这可能不是由于较低的 Android 版本中的错误。相反,这可能是因为在用户通常使用 P2P 共享应用程序的国家(例如印度),用户也更有可能使用旧版手机。

于 2018-10-31T11:28:14.213 回答
29

这有点晚了,但 Google 已经为 引入了新的 API Sideloading crash prevention,它允许您检测使用 Android App Bundle 构建的应用程序的不完整安装。

例如,考虑一个使用 Android App Bundle 来优化应用下载大小的应用,该应用使用拆分 APK。当用户从 Google Play 商店下载应用程序时,它会确保设备下载并安装在该特定设备上运行该应用程序所需的完整拆分 APK 集。当您绕过 Google Play 旁加载应用程序时,平台没有足够的数据来验证应用程序的安装,并且无法保证应用程序的正常功能。

首先在您的项目中包含 Play Core 库 1.6.0 或更高版本。

在您的应用项目的 build.gradle 文件中包含以下内容:

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

您可以使用以下 3 种方法中的一种

1) 通过清单注册检查

<?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) 在自定义应用程序类中应用检查

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

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

        super.onCreate();
        ...
    }
}

3) 对内容提供商进行检查

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

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

        super.onCreate();
        ...
    }
}

阅读更多:https ://developer.android.com/reference/com/google/android/play/core/release-notes?hl=en-419#1-6-0

于 2019-06-14T10:07:00.530 回答
16

公认的答案是绝对正确的——这个问题的根源是 APK 文件的侧载。

尽管如此,很多人仍在寻找解决方法,询问如何正确处理这种情况。

在我的应用程序中,我执行了以下操作:

  1. 创建命名为 1x1pixel.png的图像并将其放入以下所有文件夹:drawable-mdpi, drawable-hdpi, drawable-xhdpi, drawable-xxhdpi, drawable-xxxhdpi.

  2. 创建Activity显示静态消息的简单,例如

    此应用程序副本已损坏,无法启动。

    请从 Google Play 安装原始版本

  3. 然后只需getDrawable(R.drawable.pixel)Activity.onCreate()Wrapped intry/catch子句调用。

  4. 如果异常被捕获,只需完成当前Activity并从步骤#2 开始另一个。

完毕!

截屏

这个解决方案效果很好,现在我什至有来自 Firebase Analytics 的数据证实了这一点。

从 46k 的新用户(事件first_open)中,266 个用户收到了这个错误(被捕获),221 个用户点击了导致 Google Play 的按钮。

这是我的源代码(也可以在 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;
    }
}
于 2019-04-16T09:57:18.690 回答
14

问题可能是您的应用程序已被侧载,即未通过 Play 商店安装,并且已在这些设备上手动安装了不兼容的 APK。

于 2018-05-30T12:07:13.153 回答
4

由于迁移到 Android App Bundle 后仅在 Android 4 设备上发生这种情况,因此我在添加后发现了一种方法:-

public class App extends Application {

static {
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); }

在 build.gradle 中:-

android {
  defaultConfig {
    vectorDrawables.useSupportLibrary = true
  }
}

正如这篇文章中所解释的:- 在棒棒糖崩溃前使用 android vector Drawables

关于第二个问题:- 在 Android App Bundle 中添加 SwitchCompat 后,Resource Not Found 错误 res/drawable/abc_switch_thumb_material.xml

因为这发生在所有 Android 版本上。我侧载了 Apk 并能够在 logcat 中重现相同的错误,所以这只能通过从我的项目中删除 SwitchCompat 来修复,我知道它是一个临时修复,谷歌肯定应该对此做一些事情,这样至少不会发生崩溃侧载apk后,也许重定向到play store会是更好的选择。但是迁移到 Android App Bundle 后应用程序崩溃肯定会影响应用程序的稳定性,因为许多用户经常这样做。

于 2018-07-16T05:54:36.617 回答
3

您可以检查用户从播放控制台共享(旁加载)应用程序 登录播放控制台选择您发布应用程序包而不是 apk 的应用程序。

选择Android Vital -> ANRs & crashs并点击 crash 选项卡。

从播放中选择安装

在此处输入图像描述

于 2019-05-18T11:51:31.857 回答
1

对于内部测试,我们不能侧面加载 aab 文件 apks。在发布到生产环境之前,只有两种方式为用户提供 apk。

1)我们必须给universal.apk文件检查目的

java -jar bundletool-all-0.12.0.jar build-apks --bundle=(构建路径)debug.aab --output=(输出路径)debug.apks --mode=universal

2)我们必须将 aab 文件上传到 playstore,然后首先发布以进行内部测试,如果一切正常,然后将其发布到生产环境中。

于 2020-01-23T10:31:23.253 回答
0

我遇到了同样的问题,我的应用程序在无法从资源中找到字体和音频文件时崩溃。问题是我在 Gradle-wrapper.properties 中将 Gradle URL 从 4.10.1 更新到 5.6.4,但没有更新库。当我恢复到 4.10.1 时,它开始正常工作。

于 2020-05-26T12:37:43.293 回答