为什么AAssetDir_getNextFileName总是返回null?

时间:2019-04-29 21:29:06

标签: android c++ android-ndk

我正在尝试为用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指针是有效的,它可以 open 目录,但找不到任何文件或子目录。

2 个答案:

答案 0 :(得分:0)

尝试下面的代码

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 打印文件内容,如以下屏幕截图所示:

enter image description here


编辑#1:

对于您的特定情况,请尝试替换

AAssetManager *assetManager = AAssetManager_fromJava(env, assetManagerFromJava);

使用您自己的本机指针(请确保app->activity->assetManager是正确的有效指针)

AAssetManager *assetManager = app->activity->assetManager

答案 1 :(得分:0)

我已经弄清楚了。这是一个非常愚蠢的问题,我怀疑大多数问题都会遇到,但是我将在此处进行记录。

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

我不明白aapt如何打包资产文件。因此,我正在执行的是运行后生成脚本,该脚本将运行aapt add并将着色器文件添加到APK中,然后重新对齐并重新压缩APK。

再看看aapt工具的帮助命令输出,我意识到在APK编译期间有-A标志用于添加资产目录。 我最初是将资产不正确地添加到APK中

修复非常简单:

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

  • 在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并没有像我认为的那样将资产真正添加到资产目录。