80,465
社区成员




8.1.1 Constraints
There are certain constraints that you must keep in mind when writing native methods that are to run in a multithreaded environment. By understanding and programming within these constraints, your native methods will execute safely no matter how many threads simultaneously execute a given native method. For example:
A JNIEnv pointer is only valid in the thread associated with it. You must not pass this pointer from one thread to another, or cache and use it in multiple threads. The Java virtual machine passes a native method the same JNIEnv pointer in consecutive invocations from the same thread, but passes different JNIEnv pointers when invoking that native method from different threads. Avoid the common mistake of caching the JNIEnv pointer of one thread and using the pointer in another thread.
Local references are valid only in the thread that created them. You must not pass local references from one thread to another. You should always convert local references to global references whenever there is a possibility that multiple threads may use the same reference.
need to find a way to make a callback from C++ code to Java (through JNI) where the callback is on a different thread than the original call from Java that started the processing.
Let me explain that a bit more. Here's the sequence in psuedo code:
Java:
startOperation("myCallback") ; // This is a JNI call
void myCallback()
{
}
JNI/C++:
JNICALL JNI_startOperation(callbackName)
{
call-to-C-code-to-do-lots-of-stuff(&c_callback, data) ;
}
c_callback(data)
{
// Need to call back to "myCallback" in Java from here.
}
My current solution is in "JNI_startOperation" I store the JNIEnv* object I've been passed, record it with the callback and when the C++ callback occurs I retrieve it and use it to callback to Java through someting like: jenv->CallVoidMethod(). So far all is fine. The problem is if the callback is on a different thread than the original JNI_startOperation() this approach is no good because the cached JNIEnv* (I assume) is thread-specific and crashes the JVM when I try to use it.
So what I'm looking for is the proper way to look up this JNIEnv* object rather than caching it. I see methods about enumerating JVM's etc. in the JNI interface, but I'm not sure how to use them.
Anyone understand this stuff and can help walk me through it?
Doug
ANSWER
Signal 11, or officially know as "segmentation fault", means that the program accessed a memory location that was not assigned. That's usually a bug in the program. So if you're writing your own program, that's the most likely cause. However, this FAQ will concentrate on the possibilities besides that.
8.1.4 Obtaining a JNIEnv Pointer in Arbitrary Contexts
We explained earlier that a JNIEnv pointer is only valid in its associated thread. This is generally not a problem for native methods because they receive the JNIEnv pointer from the virtual machine as the first argument. Occasionally, however, it may be necessary for a piece of native code not called directly from the virtual machine to obtain the JNIEnv interface pointer that belongs to the current thread. For example, the piece of native code may belong to a "callback" function called by the operating system, in which case the JNIEnv pointer will probably not be available as an argument.
You can obtain the JNIEnv pointer for the current thread by calling the AttachCurrentThread function of the invocation interface:
JavaVM *jvm; /* already set */
f()
{
JNIEnv *env;
(*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL);
... /* use env */
}
When the current thread is already attached to the virtual machine, Attach-Current-Thread returns the JNIEnv interface pointer that belongs to the current thread.
There are many ways to obtain the JavaVM pointer: by recording it when the virtual machine is created, by querying for the created virtual machines using JNI_GetCreatedJavaVMs, by calling the JNI function GetJavaVM inside a regular native method, or by defining a JNI_OnLoad handler. Unlike the JNIEnv pointer, the JavaVM pointer remains valid across multiple threads so it can be cached in a global variable.
Java 2 SDK release 1.2 provides a new invocation interface function GetEnv so that you can check whether the current thread is attached to the virtual machine, and, if so, to return the JNIEnv pointer that belongs to the current thread. GetEnv and AttachCurrentThread are functionally equivalent if the current thread is already attached to the virtual machine.
10.14 Using the JNIEnv across Threads
The JNIEnv pointer, passed as the first argument to every native method, can only be used in the thread with which it is associated. It is wrong to cache the JNIEnv interface pointer obtained from one thread, and use that pointer in another thread. Section 8.1.4 explains how you can obtain the JNIEnv interface pointer for the current thread.
终于找到如何解读ndk运行直接挂掉之后的错误地址,以后jni执行崩溃后除了加一堆的LOG就有新的方法调试 了。
如果logcat再一次打出如下的错误:
12-1022:20:04.839 I/DEBUG ( 551):************************************************
12-1022:20:04.839 I/DEBUG ( 551):Build fingerprint:'generic/sdk/generic/:1.5/CUPCAKE/150240:eng/test-keys'
12-1022:20:04.839 I/DEBUG ( 551): pid:1003, tid:1003 >>> com.example <<<
12-1022:20:04.849 I/DEBUG ( 551): signal 11(SIGSEGV), fault addr 00000004
12-1022:20:04.849 I/DEBUG ( 551): r0 00000000 r1 0019ed48 r2 00000001 r3 0019eae4
12-1022:20:04.859 I/DEBUG ( 551): r4 0019ea9c r5 0019ed48 r6 001935b0 r7 0000a9d0
12-1022:20:04.859 I/DEBUG ( 551): r8 00000001 r9 0019eae4 10804df39c fp 0019ea9c
12-1022:20:04.869 I/DEBUG ( 551): ip 00000071 sp beb6f4a8 lr 8041d8d9 pc 8041cc6c cpsr 00000030
12-1022:20:04.948 I/DEBUG ( 551): #00 pc 0000e3b4 /system/lib/libdvm.so
12-1022:20:04.948 I/DEBUG ( 551): stack:
12-1022:20:04.948 I/DEBUG ( 551): beb6f468 00000000
12-1022:20:04.959 I/DEBUG ( 551): beb6f46c 0019ede3 [heap]
12-1022:20:04.959 I/DEBUG ( 551): beb6f470 00000002
...
如果是以前,遇到这种情况是头疼的,不知道在哪挂掉了,之前只能在代码里面一句一句加log定位。
无意中发现了 http://source.android.com/
这是个让人惊喜的网站。主要面向系统程序员;里面大量介绍了android系统移植;(暂时不关我什么事);但是里面有篇文章介绍了如果debug native:
http://source.android.com/porting/debugging_native.html
很好。耐心读完 只见最关键一步在 Use the "stack" tool to convert these addresses to files and line numbers 这个就郁闷了。什么是 "stack" tool? 去sdk以及 各个地方好像都找不到。
外事不决问google;找到http://code.google.com/p/android-ndk-stacktrace-analyzer/
OK。具体怎么使用介绍的很详细。