【问题标题】:Why does AAssetDir_getNextFileName always return null?为什么 AAssetDir_getNextFileName 总是返回 null?
【发布时间】:2019-09-18 12:46:49
【问题描述】:

我正在尝试为用 C++ 编写的 Android 应用加载一些着色器文件。它使用NativeActivitynative_app_glue。我很难让 Android Asset 系统按预期工作。

我试过的最简单的情况是:

#include <android_native_app_glue.h>

void android_main(struct android_app *app) {
  AAssetDir *dir = AAssetManager_openDir(app->activity->assetManager, "");
  if (dir != nullptr) {
    LOGD("Opened Dir");

    LOGD("Filename: %s", AAssetDir_getNextFileName(dir));

    AAssetDir_close(dir);
  } else {
    LOGE("Failed to open dir");
  }
}

AAssetManager_openDir 返回一个有效的 AAssetDir ptr,但 AAssetDir_getNextFileName 总是返回 nullptr。 NDK 的asset_manager.h 标头似乎暗示我的调用应该是绝对有效的。

/**
 * Open the named directory within the asset hierarchy.  The directory can then
 * be inspected with the AAssetDir functions.  To open the top-level directory,
 * pass in "" as the dirName.
 *
 * The object returned here should be freed by calling AAssetDir_close().
 */

我似乎也无法通过其他 AAssetManager 调用通过已知文件路径引用任何文件。

AAssetManager_open(app->activity->assetManager, "shaders/triangle.vert.spv", AASSET_MODE_BUFFER);

即使在我的 APK 中我可以清楚地看到 assets/shaders 中正确存在的文件,也会失败。我在 Logcat 中的应用中找不到任何相关错误。我在 Android 权限文档中找不到任何关于需要某种权限才能读取内部资产的相关内容。

我目前正在使用最新的 NDK r19 针对 Android 28 进行构建。这是我目前正在使用的 android-sdk 软件包集:

Installed packages:
  Path                 | Version      | Description                    | Location
  -------              | -------      | -------                        | -------
  build-tools;27.0.3   | 27.0.3       | Android SDK Build-Tools 27.0.3 | build-tools\27.0.3\
  emulator             | 28.0.25      | Android Emulator               | emulator\
  ndk-bundle           | 19.2.5345600 | NDK                            | ndk-bundle\
  patcher;v4           | 1            | SDK Patch Applier v4           | patcher\v4\
  platform-tools       | 28.0.2       | Android SDK Platform-Tools     | platform-tools\
  platforms;android-21 | 2            | Android SDK Platform 21        | platforms\android-21\
  platforms;android-28 | 6            | Android SDK Platform 28        | platforms\android-28\
  tools                | 26.0.1       | Android SDK Tools 26.0.1       | tools\

我目前通过部署到带有最新补丁的 Pixel 2 XL 进行测试。

我是否将资产放在错误的文件夹中?我错过了一些最近的权限更改吗?


编辑#1:

做了更多调查。在我的 APK 资产目录的根目录下放置了一个 test.txt,因此 APK 现在有 assets/test.txt。我试过打开这个文件的句柄:

AAsset *asset = AAssetManager_open(app->activity->assetManager, "test.txt",
                                   AASSET_MODE_BUFFER);
if (asset != nullptr) {
  LOGD("Opened test file");
  AAsset_close(asset);
} else {
  LOGE("Failed to open test file");
}

应用程序将“无法打开测试文件”打印到 Logcat。仍然不知道是什么原因造成的。


编辑#2:回复第一个答案。

按照建议尝试更完整的代码示例不会产生任何新结果:

#include <android_native_app_glue.h>
void android_main(struct android_app *app) {
  AAssetManager *assetManager = app->activity->assetManager;
  if (assetManager != nullptr) {
    AAssetDir *assetDir = AAssetManager_openDir(assetManager, "");
    if (assetDir != nullptr) {
      uint32_t fileOpenedCounter = 0;
      const char *filename = (const char *)NULL;
      while ((filename = AAssetDir_getNextFileName(assetDir)) != NULL) {
        AAsset *asset =
            AAssetManager_open(assetManager, filename, AASSET_MODE_BUFFER);
        if (asset != nullptr) {
          LOGD("Opened test file");
          fileOpenedCounter++;
          char buf[1024];
          int nb_read = 0;
          while ((nb_read = AAsset_read(asset, buf, 27)) > 0) {
            LOGD("buf: %s", buf);
          }

          AAsset_close(asset);
        } else {
          LOGE("Failed to open test file");
        }
      }
      LOGD("Opened %d files from root directory", fileOpenedCounter);
      AAssetDir_close(assetDir);
    } else {
      LOGE("Failed to open root directory");
    }
  } else {
    LOGE("Asset Manager was invalid");
  }
}

我从 Logcat 得到的只是调试日志:“从根目录打开 0 个文件”。这似乎意味着我拥有的 AAssetManager 指针是有效的,它可以 打开 目录,但找不到任何文件或子目录。

【问题讨论】:

    标签: android c++ android-ndk


    【解决方案1】:

    我已经想通了。这是一个非常愚蠢的问题,我怀疑大多数人会遇到,但我将在此处记录它。

    一些我认为不相关的背景故事。我没有使用 Android Studio。我正在使用fips 构建系统。它有一个打包 APK 的 simple python script。该脚本是从 CMake 调用的。

    我不明白 aapt 是如何打包资产文件的。所以我正在做的是运行一个构建后脚本,它将运行 aapt add 并将我的着色器文件添加到 APK 中,然后重新对齐并重新压缩 APK。

    再看一下 aapt 工具的帮助命令输出,我意识到有 -A 标志用于在 APK 编译期间添加资产目录。 我一开始就错误地将我的资产添加到 APK 中

    修复非常简单:

    • 重新路由着色器编译以构建到 fips 运行其 APK 打包的位置旁边的 assets 目录中。

    • 在fips打包脚本的AAPT打包命令中添加'-A', 'assets'

    快速浏览一下变化:

    # package the APK
    cmd = [
        AAPT,
        'package',
        '-v', '-f',
        '-S', 'res',
        '-M', 'AndroidManifest.xml',
        '-A', 'assets',
        '-I', SDK_HOME + 'platforms/android-' + args.version + '/android.jar',
        '-F', args.path + args.name + '-unaligned.apk',
        'bin'
    ]
    

    是的。这与 Android NDK API 无关,更多的是与实际的 APK 打包步骤有关。太糟糕了,NDK API 中没有任何内容可以报告找不到文件,这将是一个提示。 aapt 工具也没有警告说使用aapt add 并没有像我想象的那样真正将资产添加到资产目录中。

    【讨论】:

      【解决方案2】:

      试试下面的代码

      AAssetManager *assetManager = AAssetManager_fromJava(env, assetManagerFromJava);
      if(assetManager != nullptr) {
          AAssetDir* assetDir = AAssetManager_openDir(assetManager, "");
          const char* filename = (const char*)NULL;
          while ((filename = AAssetDir_getNextFileName(assetDir)) != NULL) {
              AAsset* asset = AAssetManager_open(assetManager, filename, AASSET_MODE_BUFFER);
              if (asset != nullptr) {
                  ALOGI("Opened test file");
                  char buf[1024];
                  int nb_read = 0;
                  while ((nb_read = AAsset_read(asset, buf, 27)) > 0) {
                      ALOGI("buf: %s", buf);
                  }
      
                  AAsset_close(asset);
              } else {
                  ALOGI("Failed to open test file");
              }
          }
          AAssetDir_close(assetDir);
      }
      

      asset 指针没问题,文件内容从 Logcat 打印出来,如下图所示:


      编辑#1:

      针对您的具体情况,尝试替换

      AAssetManager *assetManager = AAssetManager_fromJava(env, assetManagerFromJava);
      

      使用您自己的 Native 指针(请确保 app-&gt;activity-&gt;assetManager 是正确有效的指针)

      AAssetManager *assetManager = app->activity->assetManager
      

      【讨论】:

      • AAssetManager *assetManager = AAssetManager_fromJava(env, assetManagerFromJava); 行不适合我。我正在使用 NativeActivity,所以我没有任何 Java 代码。我正在使用 native_app_glue 传递给我的 android_app* 对象。该粘合层创建的 NativeActivity 有一个有效的 AAssetManager 指针,并且应该根据我一直在阅读的文档工作。除非我错过了你的 sn-p 中的一些不同的东西?
      • 我根据您提供的代码尝试了一个更完整的示例,并将其添加到主要问题中作为编辑#2。
      猜你喜欢
      • 2014-09-12
      • 2014-09-02
      • 2015-02-27
      • 2021-01-10
      • 1970-01-01
      • 2011-04-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多