I have an android app which has some JNI methods.
Each of these methods works with java reflections mostly and for that I need to have some strings (names of java methods/classes/etc - they are not constants in my case).
So I want to do some JNI initialization work which will prepare these strings before calling these JNI methods from java - I allocate (using malloc) global string variables (static char*) which will be used inside these methods whenever they called.
What is the best way to allocate/deallocate such global variables?
I could do constants but I need to modify their values at runtime (during initialization)
Right now I have initialization method which I call from Application.onCreate() - it allocates like 50 of string variables (static char*) and assigns them.
But I never call free().
Will there be any problem with this approach?
For example, when the app is killed by system, will there be any memory leak or corruption?
What if my app is started again (onCreate() called and variables are re-initialized again?)
I have not seen such problems myself, but memory is such a thing... maybe its just my luck :)
I thought of doing initialization in JNI_OnLoad, but again - when to free the resources?
Related
I have implemented a service which is invoked by system server during the boot-up.
My service has JNI implementation which creates another thread.
The reason for having another thread is to have the capability of canceling operation while monitoring the call back.
This mechanism worked well in Android Kitkat release but it crashes in Android L.
Between two threads, I stored the JavaVM* that I get from GetJavaVM(env) to a static global variable. Of course, this shared data is protected by pthread mutex.
I have tried the following so far, but all of them below still crashed :
1) Used JavaVM* I get from JNI onLoad() function by store it to global
2) In the new thread, as there is only one JavaVM running on Android, get the vm from calling android::AndroidRuntime::getJavaVM();
3) Stored the vm information in the main thread after calling NewGlobalRef(). And saved that reference to the shared data. The new thread used the reference from NewGlobalRef().
Does anyone know what is significantly changed in JNI environment on Android L release?
UPDATE :
Debugged further and the solution I mentioned 1) or 2) should have worked.
The actual issue was due to the garbage collection running more frequently. So the HAL pointer I kept was not valid any more...
These links were helpful!!!
https://developer.android.com/guide/practices/verifying-apps-art.html
http://developer.android.com/training/articles/perf-jni.html
Thanks for all the comments!
What has changed with L release is the move to ART that is less flexible than Dalvik regarding errors.
It's perfectly fine to share JavaVM* across threads, you should keep it this way.
However, what are you doing later with this JavaVM* ?
JNIEnv* has to be retrieved and used from the same thread and must not be used across threads. To use JNIEnv*, a thread must have been attached to the VM (using AttachCurrentThread).
Threads also have to be detached using DetachCurrentThread before they exit.
To maintain global variables, the Application class is extended. What happens to these variables when the application is put in the background and at some point, the OS wants to free up the resources? If the onTrimMemory() call is ignored, then does that mean that all of the variables are preserved? Unlike the Activity, which may have to rebuild some of its state.
Thanks,
Gary
To maintain global variables, the Application class is extended
You are also welcome to use classic Java static data members.
What happens to these variables when the application is put in the background and at some point, the OS wants to free up the resources?
The only "resources" that "the OS wants to free up" will be your entire process. Your static data members or other singletons (like a custom Application, or your ContentProvider instances) will remain in memory until such time as Android terminates your process.
If the onTrimMemory() call is ignored, then does that mean that all of the variables are preserved?
Yes, until such time as Android terminates your process.
I'm writing code in C++ for Android. The main code is written in Java, but I would like to start another thread invoked from JNI call and access assets from C++. However, when I pass AAssetsManager from getAssets(), it works only within scope of JNI function call. When other thread tries to use, I get an invalid pointer. I also made sure that the object AAssetsManager is not garbage collected by writing it to a static variable. Do you have any advice on how I could make it work?
In other works, I would like to access AAssetManager and cache in other object.
Thanks
It's not enough to protect the assetManager object from GC. You need a global reference:
gAssetManager = env->NewGlobalRef(assetManager);
If you wanna access Java VM by native thread, you have to call AttachCurrentThread() to attach native thread to VM. Have you made this? If not, you can refer to Oracle's JNI docs.
I have an Android application which consists of some native threads (not attached to JVM) which need to be able to call methods of a Java object.
The way in which I was intending to do this was to create a JNI function which I call from the relevant Java object which allows me to obtain and cache the required java object method ID's, JNIEnv and object references in a static native data structure so that my native threads can (thread safely) access the required methods (e.g. using (*env)->CallVoidMethod(env, this, JavaMethodID, ...), etc;
I'm not convinced this approach is going to work, since I read that the JNIEnv pointer can't be shared between threads, and that only threads which are attached to the JVM can do this kind of thing...
Is this a viable approach?
in JNI_OnLoad, cache JavaVM*. That's the only thing persistent and valid across threads.
as soon as you set up some native thread, call AttachCurrentThread and obtain JNIEnv*, which is valid only for that single thread.
with JavaVM* and JNIEnv*, look up your jclasses, jobjects and jmethodIDs. These are still valid only for the single thread you have attached to.
convert jclasses and jobjects to global references, so that it persists across threads. jmethodIDs do not need to be globalized, they are not jobjects.
On any further native threads, you again need to call AttachCurrentThread to obtain a valid JNIEnv* for that thread.
Don't forget to delete the created global references when you don't need them anymore (in JNI_OnUnload at the latest)
It annoys me when I need to pass the context reference around all over my code. So I am thinking to create a static method to return a reference to the application instance. I am not sure if it is safe to assume there is only one instance of the Application in one application. Apparently, the Application class in Android SDK doesn't provide such method to return the instance reference. So I suspect there must be a reason?
It's probably safe, assuming that your android app lives within a single os process (most do, but this isn't a guarantee on android), but I advise against it.
If you need access to the context/application outside of the places where it's already available (activities, services, broadcast receivers, applications, views, etc), you're probably letting details related to the android environment creep into code that shouldn't know so much about it.
The big exception is static utility methods (e.g. to display a canned dialog that you reuse in your app or similar), in which case passing your context is kind of a convention in the android world (for example, ProgressDialog.show takes a Context as its first argument).
While you can do this, my feeling is that it's probably a band-aid to work around the fact that you have too many components in your code that are unnecessarily tightly coupled to the android environment.