android中的JNI整理

来源:互联网 发布:机床主轴结构优化设计 编辑:程序博客网 时间:2024/06/18 08:01
JNI
java native interface 
含义: java语言 与 native语言 之间的接口
1) java中调用native接口
2) native语言中访用java中的变量和方法
==================
================== 1. java调用native接口 ==========
==================
1) 在.java文件中使用native关键字声明方法,如 android.media.MediaScanner类中: 
private static native final void native_init();
private native void processFile(String path,String mimeType,MediaScannerClient client);
2) 在.java中使用静态代码块加载对应的JNI库.
static {
System.loadLibrary("media_jni");
}
--------- 问题:
1) 在java中调用native关键字定义的方法,jvm按照什么规则找到对应的native实现呢?
native方法归根到底也是一段代码,是c/c++的一个函数,只要找到函数的入口就可以啦!!
函数的入口自然要根据函数的名称来查找了,所以这个规则即是native方法的命名规则.
---------- native方法的默认命名规则,即静态注册JNI函数的方法 -------
使用javah工具程序: javah -o [output] [packagename.classname] 例如: javah -o myjnitest.h com.example.cn.ljc.demo.jni.MainActivity
****** 以"Java_"开头,接着是java函数的全路径名,然后将所有的"."替换为"_";
****** 如果Java层函数名中如果有一个"_",转换成jni后变为"_1"
如android.media.MediaScanner类中native_init方法: 
JNIEXPORT void JNICALL Java_android_media_MediaScanner_native_1init//Java层函数名中如果有一个"_",转换成jni后变为"_1"
2) 为什么写成: System.loadLibrary("media_jni"); //系统会根据 不同的平台拓展成真实的动态库文件名,如windows平台下拓展为media_jni.dll,linux平台下拓展为media_jni.so
3) JNI函数的动态注册方法:
当Java层通过System.loadLibrary加载完JNI动态库后,紧接着会查找该库中一个叫JNI_OnLoad的函数.想使用动态注册方法,就必须实现JNI_OnLoad函数.如media_jni.so中
---------------- android_media_MediaPlayer.cpp ---> JNI_OnLoad  ---------
./frameworks/base/media/jni/android_media_MediaPlayer.cpp
------
.........
jint JNI_OnLoad(JavaVM* vm, void* reserved)//传递参数JavaVM
{
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {//从VM中取得JNIEnv的指针
LOGE("ERROR: GetEnv failed\n");
goto bail;
}
assert(env != NULL);
if (register_android_media_MediaPlayer(env) < 0) {//调用动态注册的方法
LOGE("ERROR: MediaPlayer native registration failed\n");
goto bail;
}
.........
----------------- android_media_MediaPlayer.cpp ---> register_android_media_MediaPlayer(env) 方法 ---------
./frameworks/base/media/jni/android_media_MediaPlayer.cpp
------
static int register_android_media_MediaPlayer(JNIEnv *env)
{
   return AndroidRuntime::registerNativeMethods(env,
       "android/media/MediaPlayer", gMethods, NELEM(gMethods));
}
------------ Mediaplayer.cpp中gMethods的定义: //是JNINativeMethod结构体
static JNINativeMethod gMethods[] = {
   {"setDataSource",       "(Ljava/lang/String;)V",            (void *)android_media_MediaPlayer_setDataSource},
// ========== java类中的方法名//参数列表和返回值//c++/c中的方法名
   {
"_setDataSource",
"(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V",
(void *)android_media_MediaPlayer_setDataSourceAndHeaders
   },

   {"setDataSource",       "(Ljava/io/FileDescriptor;JJ)V",    (void *)android_media_MediaPlayer_setDataSourceFD},
   {"_setVideoSurface",    "(Landroid/view/Surface;)V",        (void *)android_media_MediaPlayer_setVideoSurface},
   {"prepare",             "()V",                              (void *)android_media_MediaPlayer_prepare},
    {"prepare",             "()V",                              (void *)android_media_MediaPlayer_prepare},
   {"prepareAsync",        "()V",                              (void *)android_media_MediaPlayer_prepareAsync},
   {"_start",              "()V",                              (void *)android_media_MediaPlayer_start},
   {"_stop",               "()V",                              (void *)android_media_MediaPlayer_stop},
   {"getFrameAt",          "(I)Landroid/graphics/Bitmap;",     (void *)android_media_MediaPlayer_getFrameAt},
   {"native_invoke",       "(Landroid/os/Parcel;Landroid/os/Parcel;)I",(void *)android_media_MediaPlayer_invoke},
   {"native_setMetadataFilter", "(Landroid/os/Parcel;)I",      (void *)android_media_MediaPlayer_setMetadataFilter},
   {"native_getMetadata", "(ZZLandroid/os/Parcel;)Z",          (void *)android_media_MediaPlayer_getMetadata},
  
.....}

----------------- AndroidRuntime ---> regsterNativeMethods ---------------
./frameworks/base/core/jni/AndroidRuntime.cpp
./frameworks/base/include/android_runtime/AndroidRuntime.h
------
/*static*/ int AndroidRuntime::registerNativeMethods(JNIEnv* env,
        const char* className, const JNINativeMethod* gMethods, int numMethods)//第二个参数:className指定了java类路径,gMethods描述了方法的映射过程.
{
   return jniRegisterNativeMethods(env, className, gMethods, numMethods);
}

------------ JNIHelp.c ---> jniRegisterNativeMethods ----------
./dalvik/libnativehelper/include/nativehelper/JNIHelp.h
./dalvik/libnativehelper/JNIHelp.cpp
-------
extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
   const JNINativeMethod* gMethods, int numMethods)
{
   JNIEnv* e = reinterpret_cast<JNIEnv*>(env);

   LOGV("Registering %s natives", className);

   scoped_local_ref<jclass> c(env, findClass(env, className));
   if (c.get() == NULL) {
LOGE("Native registration unable to find class '%s', aborting", className);
abort();
   }


   if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {//调用JNIEnv的RegisterNatives方法.
LOGE("RegisterNatives failed for '%s', aborting", className);
abort();
   }

   return 0;
}
====================== //关于gMethods中第二个参数的说明: ======================
1)  "()" 中的字符表示参数列表,后面的则代表返回值。例如"()V" 就表示void Func(); "(II)V" 表示 void Func(int, int);
2)  如果Java函数的参数是class,则以"L"开头,以";"结尾,中间是用"/" 隔开的包及类名。
具体的每一个字符的对应关系如下:
字符                  native类型                  Java类型
V                       void                              void
Z                       jboolean                      boolean
I                        jint                                 int
J                       jlong                              long
D                      jdouble                        double
F                      jfloat                             float
B                      jbyte                             byte
C                      jchar                            char
S                      jshort                           short
数组则以"["开始,用两个字符表示
[I                          jintArray                          int[]
[F                        jfloatArray                       float[]
[B                        jbyteArray                       byte[]
[C                        jcharArray                      char[]
[S                        jshortArray                     short[]
[D                       jdoubleArray                 double[]
[J                        jlongArray                      long[]
[Z                        jbooleanArray               boolean[]
====================== java数据类型与native数据类型的转换 ======================
------ 基本数据类型 ------
void                          void
boolean                   jboolean
int                              jint    
long                          jlong   
double                     jdouble 
float                          jfloat  
jbyte                          jbyte   
char                          jchar   
short                          jshort
------ 引用数据类型  ------
int[]                                     jintArray
float[]                                  jfloatArray
byte[]                                  jbyteArray
char[]                                 jcharArray
short[]                                jshortArray
double[]                            jdoubleArray 
long[]                                 jlongArray
boolean[]                          jbooleanArray
java.lang.Class                          jobject
java.lang.String                          jstring
All objects                                   jobject
Object[]                          jobjectArray
java.lang.Throwable                 jthrowable


例如 //MediaScanner.java (base\media\java\android\media)841542012-11-27
private native void processFile(String path, String mimeType, MediaScannerClient client);
//android_media_MediaScanner.cpp (base\media\jni)144892012-11-27
static void android_media_MediaScanner_processFile(JNIEnv *env, jobject thiz, jstring path,jstring mimeType, jobject client)
==================
================== 2. native语言中访用java中的变量和方法 ==========
==================
这一切都要信赖于JVM提供的JNI环境: JNIEnv.JNIEnv是一个定义在jni.h中的结构体,提供了众多调用Java接口的方法:
总结其原理:C/C++要调用JAVA程序,必须先加载JAVA虚拟机,由JAVA虚拟机解释执行class文件。为了初始化JAVA虚拟机,JNI提供了一系列的接口函数,通过这些函数方便地加载虚拟机到内存中。
1). 加载虚拟机:
函数:jint JNI_CreateJavaVM(JavaVM **pvm, void **penv, void args);
参数说明:JavaVM **pvm JAVA虚拟机指针,第二个参数JNIEnv *env是贯穿整个调用过程的一个参数,因为后面的所有函数都需要这个参数,需注意的是第三个参数,在jdk1.1与1.2版本有些不同,在JDK 1.1中第三个参数总是指向一个结构JDK1_ 1InitArgs,这个结构无法完全在所有版本的虚拟机中进行无缝移植。所以为了保证可移植性,建议使用jdk1.2的方法加载虚拟机。
2). 获取指定对象的类定义:
有两种方法可获得类定义,一是在已知类名的情况使用FindClass来获取;二是通过对象直接得到类定义GetObjectClass
3). 获取要调用的方法:
获得非静态方法:
jmethodID (JNICALL *GetMethodID)(JNIEnv *env, jclass clazz, const char *name, const char *sig);
获得静态方法:
jmethodID (JNICALL *GetStaticMethodID)(JNIEnv *env, jclass class, const char *name, const char *sig);
参数说明:JNIEnv *env初始化是得到的JNI环境;jclass class前面已获取到的类定义;const char *name方法名;const char *sig方法参数定义
4). 调用JAVA类方法:
函数:CallObjectMethod(JNIEnv *env, jobject obj, jmethodID mid);
函数:CallStaticObjectMethod((JNIEnv *env, jobject obj, jmethodID mid);
5). 获得类属性的定义:
jfieldID (JNICALL *GetFieldID) (JNIEnv *env, jclass clazz, const char *name, const char *sig);
静态属性:
jfieldID (JNICALL *GetStaticFieldID) (JNIEnv *env, jclass clazz, const char *name, const char *sig);
6). 数组处理:
要创建数组首先要知道类型及长度,JNI提供了一系列的数组类型及操作的函数如:
NewIntArray、NewLongArray、NewShortArray、NewFloatArray、NewDoubleArray、 NewBooleanArray、NewStringUTF、NewCharArray、NewByteArray、NewString,访问通过 GetBooleanArrayElements、GetIntArrayElements等函数。
7). 异常:
由于调用了Java的方法,会产生异常。这些异常在C/C++中无法通过本身的异常处理机制来捕捉到,但可以通过JNI一些函数来获取Java中抛出的异常信息。
8).多线程调用
我们知道JAVA是非常消耗内存的,我们希望在多线程中能共享一个JVM虚拟机,真正消耗大量系统资源的是JAVA虚拟机jvm而不是虚拟机环境 env,jvm是允许多个线程访问的,但是虚拟机环境只能被创建它本身的线程所访问,而且每个线程必须创建自己的虚拟机环境env。JNI提供了两个函 数:AttachCurrentThread和DetachCurrentThread。便于子线程创建自己的虚拟机环境。
--------------- native方法中调用java方法 --------- 例子: 
1) jfieldID GetFieldID(jclass clazz, const char* name, const char* sig)    { return functions->GetFieldID(this, clazz, name, sig); }
jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)    { return functions->GetMethodID(this, clazz, name, sig); }
2) ------------- frameworks/base/media/jni/android_media_MediaScanner.cpp ---> MyMediaScannerClient构造器 -------
MyMediaScannerClient(JNIEnv *env, jobject client) :   mEnv(env), mClient(env->NewGlobalRef(client)),
    mScanFileMethodID(0), mHandleStringTagMethodID(0), mSetMimeTypeMethodID(0)
    {
        jclass mediaScannerClientInterface = env->FindClass(kClassMediaScannerClient);
        if (mediaScannerClientInterface == NULL) {
            LOGE("Class %s not found", kClassMediaScannerClient);

        } else {

            //获取mediaScannerClientInterface类的scanFile方法,方法签名为最后一个参数 

                mScanFileMethodID = env->GetMethodID(

                                    mediaScannerClientInterface,
                                    "scanFile",
                                    "(Ljava/lang/String;JJZZ)V");
......        }
    }

3) ---------- frameworks/base/media/jni/android_media_MediaScanner.cpp ---> scanFile 方法 ---------
virtual status_t scanFile(const char* path, long long lastModified,
            long long fileSize, bool isDirectory, bool noMedia)
    {
        jstring pathStr;
        if ((pathStr = mEnv->NewStringUTF(path)) == NULL) {
            mEnv->ExceptionClear();            return NO_MEMORY;
        }
        //调用CallVoidMethod,mClient对象的mScanFileMethodID方法,传递参数pathSrc等
        mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified,
                fileSize, isDirectory, noMedia);

        mEnv->DeleteLocalRef(pathStr);
        return checkAndClearExceptionFromCallback(mEnv, "scanFile");
    }

================ JNIEnv提供的方法很多,上面提到的只是一部分,具体可查看 jni.h中JNIEnv结构体的定义 ------ //./dalvik/libnativehelper/include/nativehelper/jni.h

参考: http://buaadallas.blog.51cto.com/399160/384611

http://blog.csdn.net/linweig/article/details/5417319

原创粉丝点击