12

In my app I have to pass a file from assets folder to shared library. I cannot do it with use of jni right now. I'm using precompiled shared library in my project, in which I have hardcoded path to my file, but I'm getting error "No such file or directory". So in my .apk file I have .so file in libs/armeabi-v7a folder and my file in /assets folder.

I have tried to do it like this:

char *cert_file = "/assets/cacert.cert";
av_strdup(cert_file);

And some other paths, but it doesn't work.

Is it possible at all?

Pavel S.
  • 302
  • 1
  • 2
  • 10

3 Answers3

21

You can simply use the AAssetManager class in C++.

Basically you need to:

  • During the init of you library get a pointer on: AAssetManager* assetManager
  • Use it to read your file:

    // Open your file
    AAsset* file = AAssetManager_open(assetManager, filePath, AASSET_MODE_BUFFER);
    // Get the file length
    size_t fileLength = AAsset_getLength(file);
    
    // Allocate memory to read your file
    char* fileContent = new char[fileLength+1];
    
    // Read your file
    AAsset_read(file, fileContent, fileLength);
    // For safety you can add a 0 terminating character at the end of your file ...
    fileContent[fileLength] = '\0';
    
    // Do whatever you want with the content of the file
    
    // Free the memoery you allocated earlier
    delete [] fileContent;
    

You can find the official ndk documentation here.

Edit: To get the AAssetManager object:

  • In a native activity, you main function as a paramater android_app* app, you just need to get it here: app->activity->assetManager
  • If you have a Java activity, you need so send throught JNI an instance of the java object AssetManager and then use the function AAssetManager_fromJava()
Sistr
  • 1,175
  • 10
  • 26
3

Your assets are packaged into your apk, so you can't refer to them directly during runtime like in the sample code you provided. You have 2 options here:

  • Process the assets as an input stream, using

Context.getAssets().open('cacert.cert')

  • Copy out your asset to a local file in your files dir, and then reference the filename of the copied file.
user868459
  • 288
  • 1
  • 3
  • 8
  • First option will not work for me now. What about second - to which location should I put this file so I will be able to access it from .so lib? – Pavel S. Apr 29 '14 at 19:01
  • 1
    Any actual file system location - internal storage, external, etc - obviously with all of the usual tradeoffs. I believe there are also ways of accessing an asset from native code, but it's not simply a "file". – Chris Stratton Apr 29 '14 at 19:08
  • So I have to use absolute path? Thanks, guys, I'll try it. – Pavel S. Apr 29 '14 at 19:15
  • Unclear what you mean. If you mean a copy you create as an actual file, then yes you need to either hand the native code a path it can use or a file descriptor. If you mean the asset itself, it does not have a path because it is not a file (though can still be accessed by special means). – Chris Stratton Apr 29 '14 at 19:40
  • Context has a getFilesDir() method to get your files dir. I recommend using this instead of hardcoding paths to your app's data dir: http://developer.android.com/reference/android/content/Context.html#getFilesDir() – user868459 Apr 29 '14 at 23:22
  • If this answered your question, you can mark this as an answer. – user868459 May 01 '14 at 06:04
  • Actually you can access in the assets in the assets folder from the ndk ... (See my answer below) – Sistr Nov 27 '15 at 17:37
  • Bundled assets do not have a file path. If the goal is to do file I/O to the asset in native C/C++ code, the only information that the main app can propagate to the JNI function is the `am` variable here `AssetManager am = this.getContext().getAssets();`. The C/C++ code shall receive this `am` variable and continue the works from there. Eventually, the file I/O in C/C++ will have this line: `int nread = AAsset_read(asset,buf, length);`. But before that, you need this: `int fd = AAsset_openFileDescriptor(asset, &offset, &length);` the `fd` if less than zero means asset is compressed. – daparic Mar 14 '20 at 16:28
0

Building off of the answer by @Sistr , I used getBuffer along with AASSET_MODE_BUFFER (instead of AAsset_read).

However, I was trying to write the buffer to an ofstream. I got signal crashes using the <<operator:

ofstream myStream(<some file>, ios_base::binary);
auto buffer = AAsset_getBuffer(asset);
myStream << buffer;

I presume this is because the buffer pointer points to the asset without a NULL character at the end (I was reading a text asset; Android source code). Using the write function worked:

ofstream myStream(<some file>, ios_base::binary);
auto buffer = AAsset_getBuffer(asset);
myStream.write((char *)buffer, AAsset_getLength(asset));

This is because the <<operator does not have to call strlen to find out how many characters to write since it is explicitly given by AAsset_getLength.

akhandok
  • 21
  • 3