Access jar file in pure Android Native project - android

I have an Android project in pure native code. Now, I need to use a third party jar file with it. How can I build my project with the jar file added?
Here, FindClass returns NULL because my jar file is not added in the JavaVM.
ANativeActivity *activity = __state->activity;
JavaVM *jvm = __state->activity->vm;
JNIEnv *env = NULL;
jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
jint res = jvm->AttachCurrentThread(&env, NULL);
jclass cls = env->FindClass("MyJavaClass"); //cls is NULL
jvm->DetachCurrentThread();
I tried creating an another jvm but it is not supported in Android. Though it's not a good idea even if it is supported.
JavaVMInitArgs vm_args;
JavaVMOption* options = new JavaVMOption[1];
std::string str = "-Djava.class.path=res/test.jar"; //Add the jar in jvm
options[0].optionString = (char*)str.c_str();
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = false;
JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
How can I add my jar correctly? I'm guessing in Android.mk file but I don't know what or how.

Related

android version 12 JNI malloc return null

Uncompress the file in the existing cpp file and create a file with a different extension.
For that reason, I call and use the cpp source with JNI. On android 11 it works fine, but on android 12 the problem occurs.
The problem is that the return of malloc in the c++ file called by JNI is null. Is there a way to use malloc in android 12?
This is part of my code.
//JAVA
String myFile = //cache memory path;
String copyFile = //cache memory path;
int res = GetFileData(myFile, copyFile);
///C++
const char* szMyFile= env->GetStringUTFChars(myFile, nullptr); //jstring -> const char*
const char* szCopyFile = env->GetStringUTFChars(copyFile, nullptr); //jstring -> const char*
///malloc
ptr_entry_info = (st_entrymodel_info *)malloc(entry_cnt * sizeof(st_entrymodel_info)); //return NULL
Edit
In the part of allocating malloc, entry_cnt indicates the number of files. entry_cnt is 1.
entry_cnt = 1;
ptr_entry_info = (st_entrymodel_info *)malloc(entry_cnt * sizeof(st_entrymodel_info)); //Not NULL
The problem is that if I initialize entry_cnt back to 1, it doesn't give me an error. But entry_cnt is already 1.

How to get Version.Code in Android using C++?

I use native C++ with my Android project.
In Java or Kotlin we can use BuildVersion.Code to get the Android code from the Gradle file. How can I get this version code in C++?
This is working for me:
jclass build_config_class = env->FindClass("com/example/app/BuildConfig");
jfieldID version_code_id = env->GetStaticFieldID(build_config_class, "VERSION_CODE", "I");
jfieldID version_name_id = env->GetStaticFieldID(build_config_class, "VERSION_NAME",
"Ljava/lang/String;");
int version_code = env->GetStaticIntField(build_config_class, version_code_id);
jstring version_name = (jstring) env->GetStaticObjectField(build_config_class, version_name_id);
You need to change com/example/app in the code to match your package name.

No implementation found when i use .so file android

i want to use native library from other project. here is my library.
it is my first time to use ndk in android studio. i succedd to load library .so, but failed when i want to access the file. here is the error
here is my java code that load the library.
here is my main java code
can you solve my problem? thanks
With the link to the example project that you provided in comments, the life is really easy.
You need the file https://github.com/CassieLuoli/react-native-smartconnection/blob/master/android/src/main/java/com/mediatek/demo/smartconnection/JniLoader.java as is in your project. Download it from GitHub without changing the class name or the package and use it in your Java app, like they do in their example.
You are missing the JNI layer in your C part. To let Java native interface GetLibVersion() to be able to find a matching function in C part, you need to define a C function name with Java_ai_widya_mediatekso_JniLoader_GetLibVersion(JNIEnv *env, jobject thiz) as the crash log told you. Don't miss the JNI parameters in the C function.
If you want to have the exact same function name in the C part as the Java part, you can register a new name to JVM. Call below function in your JNI_Onload().
static int registerNativeMethods(JNIEnv* env)
{
jclass clazz;
const char* className = "ai/widya/mediatekso/JniLoader";
clazz = env->FindClass(className);
if (clazz == NULL) {
ALOGE("Native registration unable to find class '%s'", className);
return JNI_FALSE;
}
JNINativeMethod methods[] = {
{"GetLibVersion", "()V", (void*) GetLibVersion },
{"GetProtoVersion", "()V", (void*) GetProtoVersion },
};
if (env->RegisterNatives(clazz, methods, 2) < 0) {
ALOGE("RegisterNatives failed for '%s'", className);
return JNI_FALSE;
}
return JNI_TRUE;
}
And don't forget to add the JNI parameters to in your C functions like this GetLibVersion(JNIEnv *env, jobject thiz).

Android NDK: Why is AAssetManager_open returning NULL

I have some code:
AAsset* pAsset = AAssetManager_open(pAssetManager, "asset_test.txt", AASSET_MODE_STREAMING);
DebugPrint(pAsset?"pAsset not NULL\n":"pAsset NULL");
if (pAsset)
{
char buf[1024];
AAsset_read(pAsset, buf, sizeof(buf));
DebugPrint(buf);
AAsset_close(pAsset);
}
This code always prints "pAsset NULL" in logcat.
I put the asset_test.txt file in my assets directory, and I looked in the .apk to make sure it exists by renaming the .apk to .zip and opening it with 7zip.
I have some more code:
AAssetDir* pAssetDir = AAssetManager_openDir(pAssetManager, sDirectory.c_str());
if (!pAssetDir)
{
DebugPrint("pAssetDir NULL\n");
return;
}
const char* pszDir;
while ((pszDir = AAssetDir_getNextFileName(pAssetDir)) != NULL)
{
DebugPrint(pszDir);
}
AAssetDir_close(pAssetDir);
This code prints nothing. In other words, no files are ever found in the assets directory, regardless of what paths I pass into it.
Note: DebugPrint is just a prettier looking wrapper around __android_log_print().
I passed the Activity into AAssetManager_fromJava(), while I should have passed the AssetManager in. If you pass the wrong class into AAssetManager_fromJava() it will fail without printing anything to logcat.
How to get the asset manager with JNI:
JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv();
jobject activity = (jobject)SDL_AndroidGetActivity();
jclass activity_class = env->GetObjectClass(activity);
jmethodID activity_class_getAssets = env->GetMethodID(activity_class, "getAssets", "()Landroid/content/res/AssetManager;");
jobject asset_manager = env->CallObjectMethod(activity, activity_class_getAssets); // activity.getAssets();
global_asset_manager = env->NewGlobalRef(asset_manager);
pAssetManager = AAssetManager_fromJava(env, global_asset_manager);
Stash that asset manager pointer somewhere and use it for all your AAssetManager_*() functions from now on.

fopen/fread APK Assets from NativeActivity on Android

I have only been able to find solutions dated 2010 and earlier. So I wanted to see if there was a more up-to-date stance on this.
I'd like to avoid using Java and purely use C++, to access files (some less-or-more than 1MB) stored away in the APK. Using AssetManager means I can't access files like every other file on every other operating system (including iOS).
If not, is there a method in C++ where I could somehow map fopen/fread to the AssetManager APIs?
I actually found pretty elegant answer to the problem and blogged about it here.
The summary is:
The AAssetManager API has NDK bindings. This lets you load assets from the APK.
It is possible to combine a set of functions that know how to read/write/seek against anything and disguise them as a file pointer (FILE*).
If we create a function that takes an asset name, uses AssetManager to open it, and then disguises the result as a FILE* then we have something that's very similar to fopen.
If we define a macro named fopen we can replace all uses of that function with ours instead.
My blog has a full write up and all the code you need to implement in pure C. I use this to build lua and libogg for Android.
Short answer
No. AFAIK mapping fread/fopen in C++ to AAssetManager is not possible. And if were it would probably limit you to files in the assets folder. There is however a workaround, but it's not straightforward.
Long Answer
It IS possible to access any file anywhere in the APK using zlib and libzip in C++.
Requirements : some java, zlib and/or libzip (for ease of use, so that's what I settled for). You can get libzip here: http://www.nih.at/libzip/
libzip may need some tinkering to get it to work on android, but nothing serious.
Step 1 : retrieve APK location in Java and pass to JNI/C++
String PathToAPK;
ApplicationInfo appInfo = null;
PackageManager packMgmr = parent.getPackageManager();
try {
appInfo = packMgmr.getApplicationInfo("com.your.application", 0);
} catch (NameNotFoundException e) {
e.printStackTrace();
throw new RuntimeException("Unable to locate APK...");
}
PathToAPK = appInfo.sourceDir;
Passing PathToAPK to C++/JNI
JNIEXPORT jlong JNICALL Java_com_your_app(JNIEnv *env, jobject obj, jstring PathToAPK)
{
// convert strings
const char *apk_location = env->GetStringUTFChars(PathToAPK, 0);
// Do some assigning, data init, whatever...
// insert code here
//release strings
env->ReleaseStringUTFChars(PathToAPK, apk_location);
return 0;
}
Assuming that you now have a std::string with your APK location and you have zlib on libzip working you can do something like this:
if(apk_open == false)
{
apk_file = zip_open(apk_location.c_str(), 0, NULL);
if(apk_file == NULL)
{
LOGE("Error opening APK!");
result = ASSET_APK_NOT_FOUND_ERROR;
}else
{
apk_open = true;
result = ASSET_NO_ERROR;
}
}
And to read a file from the APK:
if(apk_file != NULL){
// file you wish to read; **any** file from the APK, you're not limited to regular assets
const char *file_name = "path/to/file.png";
int file_index;
zip_file *file;
struct zip_stat file_stat;
file_index = zip_name_locate(apk_file, file_name, 0);
if(file_index == -1)
{
zip_close(apk_file);
apk_open = false;
return;
}
file = zip_fopen_index(apk_file, file_index, 0);
if(file == NULL)
{
zip_close(apk_file);
apk_open = false;
return;
}
// get the file stats
zip_stat_init(&file_stat);
zip_stat(apk_file, file_name, 0, &file_stat);
char *buffer = new char[file_stat.size];
// read the file
int result = zip_fread(file, buffer, file_stat.size);
if(result == -1)
{
delete[] buffer;
zip_fclose(file);
zip_close(apk_file);
apk_open = false;
return;
}
// do something with the file
// code goes here
// delete the buffer, close the file and apk
delete[] buffer;
zip_fclose(file);
zip_close(apk_file);
apk_open = false;
Not exactly fopen/fread but it gets the job done. It should be pretty easy to wrap this to your own file reading function to abstract the zip layer.

Categories

Resources