JNI_OnLoad is not invoked

4,906 views
Skip to first unread message

earts

unread,
Mar 22, 2011, 10:51:26 AM3/22/11
to android-ndk
I'm working on project for Android 2.3 using native activities. I
implemented JNI_OnLoad to access my Java class from C++ code:

extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
//memset(0, 0, 1024);
__android_log_write(ANDROID_LOG_INFO, "threaded_app", "JNI_OnLoad");
return JNI_VERSION_1_2;
}

The problem is that JNI_OnLoad is not invoked. There is no
"JNI_OnLoad" message in the log and no crash after uncommenting
"memset".

fadden

unread,
Mar 22, 2011, 5:54:07 PM3/22/11
to android-ndk
On Mar 22, 7:51 am, earts <artyom.yego...@gmail.com> wrote:
> The problem is that JNI_OnLoad is not invoked. There is no
> "JNI_OnLoad" message in the log and no crash after uncommenting
> "memset".

Are you sure your library is being loaded at all?

Onur Cinar

unread,
Mar 22, 2011, 7:09:00 PM3/22/11
to android-ndk

Hi,

Could you show your Java code as well? You are doing the loadLibrary
right?

Regards,

-onur

On Mar 22, 7:51 am, earts <artyom.yego...@gmail.com> wrote:

earts

unread,
Mar 23, 2011, 12:30:41 AM3/23/11
to android-ndk
On 23 мар, 04:54, fadden <fad...@android.com> wrote:
> Are you sure your library is being loaded at all?

Yes, the code in the same .cpp file is executed and my application is
running well:

//this code is not executed
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
//memset(0, 0, 1024);
__android_log_write(ANDROID_LOG_INFO, "threaded_app", "JNI_OnLoad");
return JNI_VERSION_1_1;
}

//this code is executed
void android_main(android_app* state)
{
RunMyApp();
// Make sure glue isn't stripped.
app_dummy();
}

Strange thing: after removing JNI_OnLoad function from my code the
message "No JNI_OnLoad found" doesn't appear in the log.

On 23 мар, 06:09, Onur Cinar <onur.ci...@gmail.com> wrote:
> Could you show your Java code as well?   You are doing the loadLibrary
> right?

My Java class is empty. The name of my library is specified in
AndroidManifest.xml:

<meta-data android:value="mylib"
android:name="android.app.lib_name"/>

fadden

unread,
Mar 25, 2011, 8:35:08 PM3/25/11
to android-ndk
On Mar 22, 9:30 pm, earts <artyom.yego...@gmail.com> wrote:
> On 23 мар, 04:54, fadden <fad...@android.com> wrote:
[...]
> //this code is executed
> void android_main(android_app* state)

The invocation of JNI_OnLoad is done by System.loadLibrary(). If
you're not using that to load the library, JNI_OnLoad won't happen.

If you're writing this as a native app, JNI_OnLoad isn't really
relevant.

earts

unread,
Mar 26, 2011, 11:08:15 PM3/26/11
to android-ndk
Then how can I load my java class and access it?

alan

unread,
Mar 27, 2011, 1:56:01 PM3/27/11
to andro...@googlegroups.com, earts
you can register native methods at any time, doing it inside android_main would have the same effect as doing it in jni_onload

fadden

unread,
Mar 28, 2011, 5:12:25 PM3/28/11
to android-ndk
On Mar 27, 10:56 am, alan <a...@birtles.org.uk> wrote:
> you can register native methods at any time, doing it inside android_main
> would have the same effect as doing it in jni_onload

...mostly.

I'm not sure what class loader will be available to FindClass. With
JNI_OnLoad we guarantee that the defining class loader for the class
that called System.loadLibrary() is the one that will be used. To get
the same effect with android_main you'd need to have something from
your app appear at the top of the call stack, and I don't know if
that's the case (I haven't played with it myself).

Stephen Williams

unread,
Mar 29, 2011, 10:58:58 PM3/29/11
to andro...@googlegroups.com
Is there a significant performance increase over just letting JNI scan for methods with properly formatted names?  ("Java_org_...")

The main advantage of registering functions in JNI_OnLoad() seems to be that you can map the names how you want.  Efficiency has been mentioned, but I've found no details.  Doesn't seem to be a problem, clearly the method lookup (Java->C) must be cached already.

Sounds like I need to more aggressively cache going C->Java also though.

The only things that I do in JNI_OnLoad now is to keep a reference to the VM for later use (looking up Java classes for instance) and make sure that vm->GetEnv() with a certain JNI_VER succeeds.  I could also generate register code, but that is a lot of work so it should be worth it.

sdw


--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To post to this group, send email to andro...@googlegroups.com.
To unsubscribe from this group, send email to android-ndk...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/android-ndk?hl=en.




--
--
Stephen D. Williams s...@lig.net scie...@gmail.com LinkedIn: http://sdw.st/in
V:650-450-UNIX (8649) V:866.SDW.UNIX V:703.371.9362 F:703.995.0407
AIM:sdw Skype:StephenDWilliams Resume: http://sdw.st/gres
Personal: sdw.st facebook.com/sdwlig twitter.com/scienteer

fadden

unread,
Mar 31, 2011, 8:36:58 PM3/31/11
to android-ndk
On Mar 29, 7:58 pm, Stephen Williams <scient...@gmail.com> wrote:
> Is there a significant performance increase over just letting JNI scan for
> methods with properly formatted names?  ("Java_org_...")

Probably not, unless you have a huge collection of shared libs. With
explicit registration you know the native method address but have to
search for a matching class+method in the VM; with just-in-time
registration you have the class+method but have to search through all
shared libs for an implementation. Either way it's not a huge deal
unless you've got some sort of extreme case.

> The main advantage of registering functions in JNI_OnLoad() seems to be that
> you can map the names how you want.  Efficiency has been mentioned, but I've
> found no details.  Doesn't seem to be a problem, clearly the method lookup
> (Java->C) must be cached already.

Also:
- With just-in-time registration you don't know if a method's
implementation is missing until you try to use it
- You take the hit up front, which is useful if you have a lot of
methods and their first use happens during an animation frame

> The only things that I do in JNI_OnLoad now is to keep a reference to the VM
> for later use (looking up Java classes for instance) and make sure that
> vm->GetEnv() with a certain JNI_VER succeeds.  I could also generate
> register code, but that is a lot of work so it should be worth it.

It probably isn't. If it is, you can easily add it in later.

Philippe Simons

unread,
Apr 29, 2011, 3:27:58 PM4/29/11
to andro...@googlegroups.com
I'm bumping this because I'm having a similar issue using native app...

From my native app, I'd like to fire a standard android activity using an Intent... problem, I need to get the class of my activty, but the classloader can't resolve it when I'm doing a FindClass from the native app thread...
Id like to use a SQLite database also in the native app, and to not have to write all the Java code using JNI, I created a dbHelper class but it's the same problem...

the solution seems to be to use the JNI_OnLoad to cache my class reference but JNI_OnLoad is never called, so I'm stuck!

any help would be welcome.

2011/3/26 fadden <fad...@android.com>

Stephen Williams

unread,
Apr 29, 2011, 6:33:31 PM4/29/11
to andro...@googlegroups.com
The JNIenv given to a native call often (or maybe always on Android) cannot lookup classes.  Something about it being a thread-specific instance that doesn't have a full classloader context of some kind.  You might be able to navigate to a working one somehow, however, I used the JavaVM from JNI_Onload.  If yours isn't getting called, it probably doesn't have the right name.  Are you sure you did an extern "C" to get a plain symbol?

This is working code in jni_base.cpp in JavaGlue (this version still uses the Xbig namespace).  JavaGlue inherits LGPL from XBiG (and GPL for the generator), but this code was released by me and my client as Apache 2.0 also:
#include <jni.h>
#ifdef __cplusplus
extern "C" {
#endif
    

#ifdef JNI_VERSION_1_4
#define JNI_VER JNI_VERSION_1_4
#endif
  // JDK 1.5 used JNI_VERSION 1.4!  But, just in case, keep it here.
#ifdef JNI_VERSION_1_5
#undef JNI_VER
#define JNI_VER JNI_VERSION_1_5
#endif
#ifdef JNI_VERSION_1_6
#undef JNI_VER
#define JNI_VER JNI_VERSION_1_6
#endif


    // We should have one of these so that we have access to newer JNI calls.
    // However, make sure there is only one...
    // Although careful, object caching is out of date and not needed.
    JavaVM* gJavaVM = 0;
    JNIEXPORT jint JNICALL
    JNI_OnLoad (JavaVM * vm, void * reserved) {
JNIEnv *env;
gJavaVM = vm;
if (vm->GetEnv((void**)&env, JNI_VER) != JNI_OK) {
   // DOCORE_ERROR("Failed to get the environment using GetEnv() for JNI_VER");
   return -1;
}
return JNI_VER;
    }
    
    /*
     * Convenience methods.
     *
     */
    
    // Simple helper to use global gJavaVM
    JNIEXPORT JNIEnv*
    Xbig_GetEnv() {
JNIEnv* env;
if (gJavaVM->GetEnv((void **)&env, JNI_VER)) {
   // log("Failed to get the environment using GetEnv() for JNI_VER");
   return NULL;
}
return env;
    }
    // Need error checking / logging...
    JNIEXPORT jmethodID
    Xbig_cpath2MID(const char* cpath, const char* meth, const char* sig) {
JNIEnv* env = Xbig_GetEnv();
return Xbig_cpath2MIDenv(env, cpath, meth, sig);
    }
    JNIEXPORT jmethodID
    Xbig_cpath2MIDenv(JNIEnv* env, const char* cpath, const char* meth, const char* sig) {
jclass cls = env->FindClass(cpath);
if (!cls) {fprintf(stderr, "Class was 0 for: %s\n",  cpath); return 0;}
jmethodID mid = env->GetMethodID(cls, meth, sig);
// message("DeleteLocalRef class");
env->DeleteLocalRef(cls);
if (!mid) { fprintf(stderr, "MID was 0 for: %s\n",  meth); fflush(stderr); }
return mid;
    }
    JNIEXPORT jfieldID
    Xbig_cpath2FIDenv(JNIEnv* env, const char* cpath, const char* field, const char* sig) {
jclass cls = env->FindClass(cpath);
if (!cls) {fprintf(stderr, "Class was 0 for: %s\n",  cpath); return 0;}
jfieldID fid = env->GetFieldID(cls, field, sig);
// message("DeleteLocalRef class");
env->DeleteLocalRef(cls);
if (!fid) { fprintf(stderr, "FID was 0 for: %s\n",  field); fflush(stderr); }
return fid;
    }
    JNIEXPORT jmethodID
    Xbig_obj2MID(jobject obj, const char* meth, const char* sig) {
JNIEnv* env = Xbig_GetEnv();
return Xbig_obj2MIDenv(env, obj, meth, sig);
    }
    JNIEXPORT jmethodID
    Xbig_obj2MIDenv(JNIEnv* env, jobject obj, const char* meth, const char* sig) {
jclass cls = env->GetObjectClass(obj);
// message("DeleteLocalRef class");
env->DeleteLocalRef(cls);
jmethodID mid = env->GetMethodID(cls, meth, sig);
return mid;
    }
#ifdef __cplusplus
}
#endif

sdw


2011/4/29 Philippe Simons <simons....@gmail.com>

I'm bumping this because I'm having a similar issue using native app...

From my native app, I'd like to fire a standard android activity using an Intent... problem, I need to get the class of my activty, but the classloader can't resolve it when I'm doing a FindClass from the native app thread...
Id like to use a SQLite database also in the native app, and to not have to write all the Java code using JNI, I created a dbHelper class but it's the same problem...

the solution seems to be to use the JNI_OnLoad to cache my class reference but JNI_OnLoad is never called, so I'm stuck!

any help would be welcome.


2011/3/26 fadden <fad...@android.com>
On Mar 22, 9:30 pm, earts <artyom.yego...@gmail.com> wrote:
> On 23 мар, 04:54, fadden <fad...@android.com> wrote:
[...]
> //this code is executed
> void android_main(android_app* state)

The invocation of JNI_OnLoad is done by System.loadLibrary().  If
you're not using that to load the library, JNI_OnLoad won't happen.

If you're writing this as a native app, JNI_OnLoad isn't really
relevant.


--

Zoran Angelov

unread,
Apr 30, 2011, 3:46:22 AM4/30/11
to andro...@googlegroups.com
@earts:
the name of the .so file is defined in Android.mk file LOCAL_MODULE
variable, not in manifest.

@Philippe Simons:
i had same issues with FindClass from native thread.

Solution was:

1. Cache the instance of java.lang.ClassLoader (with
java.lang.Thread.currentThread().getContextClassLoader()), its jclass
and java.lang.ClassLoader.loadClass() in JNI_OnLoad;
2. Attach the native thread with AttachCurrentThread;
3. When you need to find new classes from native threads use
java.lang.ClassLoader.loadClass() (cast the result of loadClass() to
jclass);

Philippe Simons

unread,
Apr 30, 2011, 4:04:11 PM4/30/11
to andro...@googlegroups.com
yep but JNI_OnLoad is never called using a NativeActivty and the android_native_app_glue

so I can't cache the reference of the ClassLoader

any solution?

2011/4/30 Zoran Angelov <bal...@gmail.com>

Philippe Simons

unread,
Apr 30, 2011, 4:26:13 PM4/30/11
to andro...@googlegroups.com
just found the methode getClassLoader from Activity class...
gonna try this

2011/4/30 Philippe Simons <simons....@gmail.com>

Philippe Simons

unread,
Apr 30, 2011, 4:37:53 PM4/30/11
to andro...@googlegroups.com
yep that's working from the native thread

2011/4/30 Philippe Simons <simons....@gmail.com>

Zoran Angelov

unread,
May 3, 2011, 3:08:57 AM5/3/11
to andro...@googlegroups.com
Hi,
you are right,
it seems that when using android_native_app_glue, the main entry
point of native application is android_main.
Reply all
Reply to author
Forward
0 new messages