返回

Flutter应用被Google Play排除设备?原因及解决全攻略

Android

解决 Flutter 应用在 Google Play 控制台中被排除设备的问题

在 Google Play 控制台中发布 Flutter 应用时,可能会遇到部分设备被排除的情况,如提问者提供的截图中显示的 “xiaomi_ginkgo” 设备被排除。这表示你的应用无法在这些设备上安装或运行。

出现这种情况,通常是因为应用构建配置或代码不满足目标设备的特定要求,触发了 Google Play 的自动排除规则。要解决该问题,你需要了解排除原因并调整应用的配置。

一、理解设备排除规则

Google Play 控制台设备目录基于应用的清单文件(manifest file)、Gradle 文件及应用的其他配置信息来确定哪些设备可以安装并运行你的应用。如果目标设备在系统功能、API 级别或其他硬件/软件特性上不满足应用的最小要求,Google Play 会将其排除在可兼容设备列表之外。

当特定设备被排除时,设备目录页面会显示简要的排除原因,比如:

  • “This app won't work on this device.” (应用在此设备上无法正常运行): 表示在目标设备运行失败或不符合应用的要求,触发了谷歌 Play 的自动排除规则。
  • “RAM (小于 xxxxMB)” (RAM小于指定大小): 表明你的应用需要的运行内存大小高于设备的实际物理内存。
  • “ABIs” (不支持的 CPU 架构): 表示应用不支持设备的 CPU 架构 (ABI)。

安全建议 : 排除一些低于指定条件的设备能够防止应用因为运行不流畅或者不兼容带来的用户差评, 以及潜在的应用崩溃, 数据丢失以及应用损坏的风险。所以, 应用不应刻意适配所有低配置或者兼容性有问题的设备, 如果必须, 需要严格的质量测试。

二、常见原因及解决方案

遇到设备被排除的问题,一般由以下几种原因导致。以下分别介绍,并提供具体的解决办法。

1. 不支持的 CPU 架构 (ABI)

原因分析 :

应用使用了某些 native 库,但生成的 appbundle 只包含了部分 CPU 架构的支持,而未包含目标设备支持的架构。

解决办法:

1. 确认目标设备的 CPU 架构。

可以通过连接设备使用 ADB 指令查看:

adb shell getprop ro.product.cpu.abi

该指令会输出类似 arm64-v8a 的结果,这就是设备的 CPU 架构。

2. 确保构建脚本生成了目标设备架构的 App Bundle。

如果使用 flutter build appbundle 命令构建应用,可以检查你的项目android/app/build.gradle 文件中关于 splits.abi.include 的配置,或者,通过 flutter.gradle 默认包含。它控制要构建的架构列表。默认情况,Flutter 自动配置构建针对 arm64-v8aarmeabi-v7ax86-64 架构的输出。

例如:你的应用集成了只支持armeabi-v7a 的so库。同时你也使用了不支持 x86_64的so库,目标设备需要支持armeabi-v7a, 则可以通过下面的 build.gradle 来只生成对应支持的架构。

android {
    ...
    splits {
        abi {
            enable true // enabling the NDK split 
            reset() //reset
            include 'arm64-v8a', 'armeabi-v7a'  // specify your chosen ABIs, remove others from generated config
            universalApk false //build with only common and your current chosen abi
        }
    }
    ...
}

注意 ,你需要将你的第三方SDK以及使用的so库加入考虑范围。保证其对应的so支持你需要生成的cpu架构。你可以在第三方SDK文档或者开发支持寻求这方面的信息。

如果需要通过gradle直接修改目标 ABI 的支持。直接修改build.gradle中的ndk.abiFilters

defaultConfig {
  // ..
    ndk {
       // 指定了 ABI 过滤列表, 会覆盖flutter 的 abi 设置. 这样的话需要保证你的应用同时需要能够支持x86_64, 例如在支持x86_64的设备模拟器测试应用的功能性
       abiFilters "arm64-v8a", "armeabi-v7a"  
       // 推荐做法:移除对不支持的so的支持,这样用户就能减少下载应用的size
    }
}

安全建议 : 对于每个集成的外部native库,开发者需要对支持的所有架构都进行全面的功能测试以及应用崩溃检测。在应用发布之前一定要使用测试版进行质量审核,保证功能和性能稳定性。

2. 最低 API 级别不满足要求

原因分析:

应用的 minSdkVersion 设置高于目标设备的 Android 系统版本。

解决办法:

  1. 确定目标设备的 API 级别。
    通过 ADB 命令查看:

    adb shell getprop ro.build.version.sdk
    

    通过此指令输出的数值即是api 的级别。

  2. 确认和设备兼容的 API 级别是否匹配你的用户场景。

  3. 调整 minSdkVersion

    修改应用 android/app/build.gradle 文件中的 minSdkVersion 值, 使用支持该设备运行的最高版本的 API 级别。在问题中给出的例子中, 它设置为了 23, 也就是 Android 6.0 的API Level. 但是因为你设置的ndkVersion版本太高, 你需要将其降到 21, 或者提高 minSdkVersion, 这里面需要你的斟酌取舍。比如你的应用只适配安卓 6.0以上的用户, 你的应用的最小支持就可调整到23. 当然, 这里还要兼顾 targetSdkVersion, 一般保持一致。

    android {
        ...
        defaultConfig {
            ...
            minSdkVersion 23  // or a different API level number
            targetSdkVersion 35
        }
        ...
    }
    
  4. 重新编译并上传:
    使用修改后的 build.gradle 重新构建你的App Bundle:

    flutter build appbundle --obfuscate --release --split-debug-info=<路径>
    
  5. 将重新构建的应用上传到控制台测试。

3. 不支持的 OpenGL ES 版本

原因分析:

应用使用了高版本的 OpenGL ES 特性,而目标设备不支持。

解决办法:

  1. 声明所需的 OpenGL ES 版本。

    AndroidManifest.xml 中添加 <uses-feature> 标签声明所需的 OpenGL ES 版本。这样一来, 不支持这个版本及以上 OpenGL ES 版本的设备就直接会被标记为不兼容, 这也有助于提升应用的运行流畅度, 不再尝试在此设备兼容该应用。

    <manifest ...>
        <uses-feature android:glEsVersion="0x00020000" android:required="true" />  <!-- For OpenGL ES 2.0 -->
        <!-- <uses-feature android:glEsVersion="0x00030000" android:required="true" /> --> <!-- For OpenGL ES 3.0 -->
        ...
    </manifest>
    
  2. 注意: 不要因为使用了flutter而放弃添加该项, 如果 flutter 应用没有依赖外部组件而是只依赖自己的engine和framework, 这会保证被正常渲染。

  3. 检查项目中 native 代码以及是否在pubspec.yaml 中使用的 OpenGL ES 版本, 做出合理的取舍 。通常你必须保证应用中要求的最低版本和此处 AndroidManifest.xml 声明的OpenGL ES版本以及pubspec.yaml的约束的版本一致。

  4. 确保所有 OpenGL ES 相关的功能在不同版本的设备上都经过测试,可以在不同版本或者使用相同 OpenGL ES 版本的设备做测试,避免崩溃和其他的运行故障.

4. 特定功能或权限限制

原因分析:

应用使用了目标设备不支持的功能,例如在manifest中声明了不支持的传感器,权限或者其他的硬件支持等等,也会导致设备被排除。

解决办法:

  1. 审查 AndroidManifest.xml
    检查是否有不必要的 <uses-feature><uses-permission> 声明,尤其是设置了 android:required="true" 的功能,或者不合理的高危权限申请. 可以考虑移除这些功能, 特别是对一些高权限的API需要小心, 这些有可能引发google的严格审核. 如果没有严格必要(不使用则会引发致命的故障, 而不仅仅是降低了应用的易用性), 可以选择移除。

  2. 条件化地使用某些功能。

    对于非必需的功能,可以使用 PackageManagerhasSystemFeature() 方法进行判断,只有当设备支持时才启用相应功能,例如判断设备支持 android.hardware.bluetoothandroid.hardware.nfc等等:

    import 'package:package_info_plus/package_info_plus.dart';
    
    ...
    
    bool hasBluetooth;
    bool hasNfc;
    try {
      final info = await PackageInfo.fromPlatform();
      hasBluetooth = await info.hasSystemFeature('android.hardware.bluetooth');
      hasNfc = await info.hasSystemFeature('android.hardware.nfc');
    } catch(e){
        // catch errors 
    }
    if (hasBluetooth) {
      // Enable Bluetooth features
    }
    
    if (hasNfc) {
      // Enable NFC features
    }
    

安全建议 : 谨慎使用高危权限, 同时避免给非强制性需求设置 android:required="true" 的情况. 这两者都会减少兼容设备数量。

三、总结以及最佳实践

以上几个步骤包含了 Flutter 应用被 Google Play 控制台排除设备的几种主要情况和通用性的解决方案。

  • 需要记住: 当排除某个设备的时候需要慎重, 需要充分考虑用户受众范围,尽量实现用户范围和稳定, 安全与易用性多方面平衡, 减少用户无法使用带来的困扰.

  • Google Play 控制台提供了强大的设备管理功能。需要理解规则, 理解导致你的Flutter App被排除在设备列表以外的原因以及背后的解决逻辑, 通过查看具体设备支持的信息并排查代码来解决问题. 有针对性地解决该问题才能成功上传并上线你的 Flutter 应用, 为应用赢得更广阔的潜在用户。