Получение имени Android APK с использованием C++ и класса NativeActivity

Я пишу приложение для Android, используя NDK и NativeActivity. Мое приложение зависит от нескольких битов стороннего кода, которые поставляются как активы. В настоящее время я работаю над тем, чтобы извлечь эти ресурсы, сохраняя структуру папок без изменений.

Я пытался использовать AssetManager, но чтобы сохранить структуру папок нетронутой, казалось, что потребуется огромное количество кода для простой задачи, такой как я упомянул. С тех пор я переключил фокус, чтобы попытаться реализовать обработку APK как файла ZIP и извлекать его содержимое таким образом. Но для этого нужно найти точный путь к APK.

В обычном приложении для Android можно использовать getPackageCodePath, но это абстрактный метод, прикрепленный к классу Context. У меня вопрос, как мне получить точный путь к APK, если не используется обычная активность?

Также я попытался вызвать getPackageCodePath через JNI, но это привело к сбою приложения из-за невозможности найти метод.

РЕДАКТИРОВАТЬ: это вообще возможно?

5 ответов

Решение

Я был действительно в состоянии позвонить getPackageCodePath через JNI и заставить его работать. Следующий код ставится вверху android_main в образце native-активности в NDK r7 записывает правильный путь и не вылетает:

void android_main(struct android_app* state) {
    struct engine engine;

    ANativeActivity* activity = state->activity;
    JNIEnv* env = activity->env;

    jclass clazz = (*env)->GetObjectClass(env, activity->clazz);
    jmethodID methodID = (*env)->GetMethodID(env, clazz, "getPackageCodePath", "()Ljava/lang/String;");
    jobject result = (*env)->CallObjectMethod(env, activity->clazz, methodID);

    const char* str;
    jboolean isCopy;
    str = (*env)->GetStringUTFChars(env, (jstring)result, &isCopy);
    LOGI("Looked up package code path: %s", str);

    ...
}

Я чувствую, что это может быть не лучшим решением, хотя. Меня волнуют две вещи:

  1. Безопасность потоков - есть ужасное предупреждение только об использовании env член ANativeActivity в основном потоке Java, и если я правильно понимаю, этот код будет запущен в потоке нативного действия.
  2. ANativeActivity"s clazz член, кажется, неправильно назван и на самом деле является экземпляром Java NativeActivity вместо объекта класса. В противном случае этот код не будет работать. Я действительно ненавижу полагаться на что-то, что явно названо так.

Кроме того, это работает, и я собираюсь использовать его сам, чтобы попытаться извлечь ресурсы из.apk с помощью libzip в каталог данных.

Поскольку мне просто нужно было найти, как именно выполнять вызовы присоединения / отсоединения, я вставлю обновленную версию здесь.

Следующее, кажется, получить правильное местоположение без сбоев (после минимального тестирования)

    ANativeActivity* activity = state->activity;
    JNIEnv* env=0;

    (*activity->vm)->AttachCurrentThread(activity->vm, &env, 0);

    jclass clazz = (*env)->GetObjectClass(env, activity->clazz);
    jmethodID methodID = (*env)->GetMethodID(env, clazz, "getPackageCodePath", "()Ljava/lang/String;");
    jobject result = (*env)->CallObjectMethod(env, activity->clazz, methodID);

    const char* str;
    jboolean isCopy;
    str = (*env)->GetStringUTFChars(env, (jstring)result, &isCopy);
    LOGI("Looked up package code path: %s", str);

    (*activity->vm)->DetachCurrentThread(activity->vm);

Вы пытались прочитать /proc/self/cmdline из своего приложения? Вы должны быть в состоянии открыть его как обычно (насколько нормальны файлы proc:-), так что вы можете читать из файла до EOF, но не искать) c FILE и читать из него.

В качестве примера для приложения для телефона я могу видеть из ps в Android, что имя приложения - это ожидаемое имя приложения:

 # ps | grep phone 
 radio     1588  839   1467420 103740 SyS_epoll_ 7f7de374ac S com.android.phone

А проверка cmdline для этого pid возвращает хорошее имя приложения:

 # cat /proc/1588/cmdline
 com.android.phone

Пришлось доработать это к этому в 2014 году.

ANativeActivity* activity = state->activity;
JNIEnv* env=0;

activity->vm->AttachCurrentThread(&env, NULL);

jclass clazz = env->GetObjectClass(activity->clazz);
jmethodID methodID = env->GetMethodID(clazz, "getPackageCodePath", "()Ljava/lang/String;");
jobject result = env->CallObjectMethod(activity->clazz, methodID);

jboolean isCopy;
std::string res = env->GetStringUTFChars((jstring)result, &isCopy);
LOG_DEBUG("Looked up package code path: %s", res.c_str());

activity->vm->DetachCurrentThread();

Вызов getPackageCodePath() в Java и передать Jstring в ваше приложение C++ через нативный метод

Другие вопросы по тегам