Получение имени 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);
...
}
Я чувствую, что это может быть не лучшим решением, хотя. Меня волнуют две вещи:
- Безопасность потоков - есть ужасное предупреждение только об использовании
env
членANativeActivity
в основном потоке Java, и если я правильно понимаю, этот код будет запущен в потоке нативного действия. ANativeActivity
"sclazz
член, кажется, неправильно назван и на самом деле является экземпляром JavaNativeActivity
вместо объекта класса. В противном случае этот код не будет работать. Я действительно ненавижу полагаться на что-то, что явно названо так.
Кроме того, это работает, и я собираюсь использовать его сам, чтобы попытаться извлечь ресурсы из.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++ через нативный метод