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).
I built tinyalsa lib using the sources from tinyalsa-ndk and wrapped it with JNI calls and I'm trying to use it in my code.
I used Swig to generate Java wrappers (and modified output to comply with my package)
My native method declaration is:
public final static native long mixer_open(long jarg);
My JNI wrapper call is inside a wrapper class TinyAlsa.java under the root pacakge (for the example I'll use com.Example.App):
public static SWIGTYPE_p_mixer mixer_open(long card)
{
long cPtr = TinyAlsaJNI.mixer_open(card);
return (cPtr == 0) ? null : new SWIGTYPE_p_mixer(cPtr, false);
}
and my wrapper c method is:
SWIGEXPORT jlong JNICALL Java_com_Example_App_Native_TinyAlsaJNI_mixer_1open(JNIEnv *jenv, jclass jcls, jlong jarg1)
{
jlong jresult = 0 ;
unsigned int arg1 ;
struct mixer *result = 0 ;
(void)jenv;
(void)jcls;
arg1 = (unsigned int)jarg1;
result = (struct mixer *)mixer_open(arg1);
*(struct mixer **)&jresult = result;
return jresult;
}
The tinalsa library is loaded OK without exceptions but calls such as mixer_open(0) returns null pointers.
However If I execute the compiled tinymix mixer is open and mixer controls are listed as should.
Am I missing something? How can I make it work from my code?
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.
This is a follow-up to another question I asked: Android -- get MEID from JNI
I am trying to get the ID of a phone in Android. I have some JNI code and a simple test app to call the JNI code. Here is working Java code from my simple test app:
TelephonyManager tm = (TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE);
String id = tm.getDeviceId();
The string id is set to the phone ID value that I want. But I need to get it from JNI, and just using the above code and passing the ID value in is not an acceptable solution. (This is to make some JNI code somewhat tamper-proof and I shouldn't trust the Java layer to send a correct ID value.)
Here is the JNI code that I have written, with error handling removed so it is easier to follow. It works until the indicated line, and then the whole app crashes.
// "env" and "obj" are passed to a JNI function and are used unmodified in this code
// JNIEnv *env, jobject obj
jclass cls_context = NULL;
jclass cls_tm = NULL;
jobject tm = NULL;
jmethodID mid;
jfieldID fid;
jstring jstr;
jsize len_jstr;
cls_context = (*env)->FindClass(env, "android/content/Context");
fid = (*env)->GetStaticFieldID(env, cls_context, "TELEPHONY_SERVICE",
"Ljava/lang/String;");
jstr = (*env)->GetStaticObjectField(env, cls_context, fid);
mid = (*env)->GetMethodID(env, cls_context, "getSystemService",
"(Ljava/lang/String;)Ljava/lang/Object;");
tm = (*env)->CallObjectMethod(env, obj, mid, jstr); // THIS LINE CRASHES
cls_tm = (*env)->FindClass(env, "android/telephony/TelephonyManager");
mid = (*env)->GetMethodID(env, cls_tm, "getDeviceId",
"()Ljava/lang/String;");
jstr = (*env)->CallObjectMethod(env, tm, mid);
len_jstr = (*env)->GetStringUTFLength(env, jstr);
(*env)->GetStringUTFRegion(env, jstr, 0, len_jstr, buf_devid);
I think the problem is that obj isn't the right thing to pass, but if so I have no idea what is the right thing. Isn't obj that gets passed to the JNI function the same thing as this in the Java code?
EDIT: Okay, we have figured out that if we add an extra argument of type jobject to the JNI function, and explicitly pass a copy of this in that argument, and then pass that to CallObjectMethod() (the one that crashes in the above code), everything works. We get our TelephonyManager instance and we can query the Phone ID value.
Using a logging macro, I logged the obj pointer and the passed-in this pointer. They are similar numbers (addresses close to each other) but not identical. So I think obj is some sort of object reference from inside the Java VM... it is not actually the same as this.
In a JNI function, the first two arguments are JNIEnv *env and jobject obj. What is that second one for? What can I do with it? Is there any way I can use it to call getSystemService or will I have to pass an extra argument and pass in this?
The problem seems to be related to Java inheritance: in Java, you can call this.getSystemService() and it works, even though this is not actually an instance of Context. When you make the JNI call, the call simply fails.
So our solution was to have our Android app add a .getApplicationContext() method function actually as part of its own class. This in turn calls the actual getSystemService() and returns the result.
The code didn't change: we are still calling
mid = (*env)->GetMethodID(env, cls_context, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
but now it works, when there is a .getSystemService() method in the class that is calling our JNI function. So, the env argument in a JNI call does represent this (it's not identical... I printed the value of this as a pointer, and printed env, and they are not the same but they are definitely related).
If you want to call a superclass method on object, you should use CallNonvirtualObjectMethod()
How to call an overriden method in JNI
Try this... It works... "context" came from java :)
jclass ctx = env->FindClass("android/content/Context");
jfieldID fid = env->GetStaticFieldID(ctx,"TELEPHONY_SERVICE","Ljava/lang/String;");
jstring str = (jstring) env->GetStaticObjectField(ctx, fid);
jmethodID mid = env->GetMethodID(ctx, "getSystemService","(Ljava/lang/String;)Ljava/lang/Object;");
jobject tm = env->CallObjectMethod(context, mid, str);
jclass ctx_tm = env->FindClass("android/telephony/TelephonyManager");
jmethodID mid_tm = env->GetMethodID(ctx_tm,"getDeviceId","()Ljava/lang/String;");
jstring str_tm = (jstring) env->CallObjectMethod(tm, mid_tm);
strReturn = env->GetStringUTFChars(str_tm, 0);
I would like to change
Cipher cipher = Cipher.getInstance("DES");
into cpp code in jni.How to do it?
JNIEnv *jni; //Comes from somewhere
jclass cl = jni->FindClass("javax/crypto/Cipher");
jmethodID MID = jni->GetStaticMethodID(cl, "getInstance", "(Ljava/lang/String)Ljavax/crypto/Cipher;");
jstring s = jni->NewStringUTF("DES");
jobject cipher = jni->CallStaticObjectMethod(cl, MID, s);
That's omitting error handling.
Another alternative would be to use native code for encryption, e.g. OpenSSL.
Mandatory warning: don't use DES.