Qt for Android: get access to the AssetManager



  • Hi,
    we have an OpenGL rendering engine working on Qt Mac/Linux/iOS apps. Now we are porting to Android, and we are having problems with the assets. They seem to be compressed in the apk, and we need them unpacked, and at the same directory as the app executable file (or .so lib).

    We have read about this piece of code, but in order to make it work, we need access to the app object, which can be obtained using the android_native_app_glue framework.

    @
    AAssetManager* mgr = app->activity->assetManager;
    AAssetDir* assetDir = AAssetManager_openDir(mgr, "");
    const char* filename = (const char*)NULL;
    while ((filename = AAssetDir_getNextFileName(assetDir)) != NULL) {
    AAsset* asset = AAssetManager_open(mgr, filename, AASSET_MODE_STREAMING);
    char buf[BUFSIZ];
    int nb_read = 0;
    FILE* out = fopen(filename, "w");
    while ((nb_read = AAsset_read(asset, buf, BUFSIZ)) > 0)
    fwrite(buf, nb_read, 1, out);
    fclose(out);
    AAsset_close(asset);
    }
    AAssetDir_close(assetDir);
    @
    Taken from here: http://stackoverflow.com/questions/13317387/how-to-get-file-in-assets-from-android-ndk

    Any ideas on how could we access the assets manager and decompress all the assets we need?

    We are using Qt 5.4 beta, and OSX 10.9.

    Thanks,
    Robert.



  • Do you know that Qt support a way to access file contained into asset by specifying path in the following way:
    @
    QFile file( "assets:/path/inside/asset/dir/filename.txt" )
    @



  • Oops, you're right.
    Anyway, finally I developed a simple Activity inheriting from QtActivity, that loads a .so library and sends the AssetsManager to the C++ part, then I iterate over the assets and put them in the correct place for our engine to work.

    Obviously accessing the Qt resources seems to be a better approach.

    Thanks, anyway.


  • Qt Champions 2016

    Please keep in mind that Qt's simulation of assets resource is pretty slow , especially for directory listing. If your application need to extract too many files, you may need to go back to Java.



  • @che1404 Hello, I'm working on a framework to develop applications with Ogre3d and Qt. It's compiled on Windows/Linux and now we want to compile it for Android. I have the same problem than you. Ogre needs access to a pointer of AAssetManager that you talk about. I've been trying everything but I cant get it work. Can you please give me some ideas or show me some code to solve the problem?
    Thanks



  • After a long search I found the solution in the code of Qt itself. The final result is a modification of the original code found here: [QT_SOURCE] /qtbase/src/plugins/platforms/android/src/androidjnimain.cpp. You should put the code in a cpp of the application that needs the pointer to AAssetManager.

    @
    #include <jni.h>
    #include <android/asset_manager_jni.h>

    static JavaVM* javaVM = NULL;
    static AAssetManager* m_assetManager = NULL;

    static jclass m_applicationClass = NULL;
    static jobject m_activityObject = NULL;

    #define MY_FIND_AND_CHECK_CLASS(CLASS_NAME)
    clazz = env->FindClass(CLASS_NAME);
    if (!clazz) {
    qDebug() << "No se encontró la clase :(";
    return JNI_FALSE;
    }

    #define MY_GET_AND_CHECK_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE)
    VAR = env->GetMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE);
    if (!VAR) {
    qDebug() << "No se encontró el metodo :(";
    return JNI_FALSE;
    }

    #define MY_GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE)
    VAR = env->GetStaticMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE);
    if (!VAR) {
    qDebug() << "No se encontró el metodo estatico :(";
    return JNI_FALSE;
    }

    static int findAssets(JNIEnv *env)
    {
    jclass clazz;

    MY_FIND_AND_CHECK_CLASS("org/qtproject/qt5/android/QtNative");
    m_applicationClass = static_cast<jclass>(env->NewGlobalRef(clazz));
    
    jmethodID methodID;
    
    MY_GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "activity", "()Landroid/app/Activity;");
    jobject activityObject = env->CallStaticObjectMethod(m_applicationClass, methodID);
    m_activityObject = env->NewGlobalRef(activityObject);
    
    MY_FIND_AND_CHECK_CLASS("android/content/ContextWrapper");
    MY_GET_AND_CHECK_METHOD(methodID, clazz, "getAssets", "()Landroid/content/res/AssetManager;");
    m_assetManager = AAssetManager_fromJava(env, env->CallObjectMethod(activityObject, methodID));
    
    return JNI_TRUE;
    

    }

    extern "C"
    {
    JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void */reserved/)
    {
    qDebug() << "JNI_OnLoad :)";

        typedef union {
            JNIEnv *nativeEnvironment;
            void *venv;
        } UnionJNIEnvToVoid;
    
        qDebug() << "UNION declarada :)";
    
        UnionJNIEnvToVoid uenv;
        uenv.venv = NULL;
        javaVM = 0;
    
        if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {
            qDebug() << "No hay puntero al ambiente :(";
            return -1;
        }
    
        qDebug() << "Hay puntero al ambiente :)";
    
        JNIEnv *env = uenv.nativeEnvironment;
        if (!findAssets(env)) {
            qDebug() << "No se obtuvo puntero a al AAssetManager :(";
            return -1;
        }
        qDebug() << "Fuck yea!!! :)";
    
        javaVM = vm;
        return JNI_VERSION_1_4;
    }
    

    }
    @


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.