`
java-mans
  • 浏览: 11413413 次
文章分类
社区版块
存档分类
最新评论

JNI系统程序开发

 
阅读更多
JNI是Java Native Interface的缩写。从Java 1.1开始,JNI标准成为java平台的一部分,它允许Java和其他语言进行交互。
JNI一开始为C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,它可以直接与硬件、操作系统进行交互,也能提高程序的性能。
Android中JNI系统程序和应用程序的开发有些细微的差别。本文章主要是针对Android中JNI系统程序的开发,如果你想进步了解应用程序请阅读《JNI应用程序开发
Jni程序开发的一般操作步骤如下:
(1) 编写带有native声明的方法的java类
(2) 编译java类成class文件,使用javah -jni java类名生成扩展名为h的头文件
(3)使用C++ 实现本地方法
(4)编辑Android.mk
(5)编译JNI的C/C++源码生成.so文件
(6) 在Java中把.so文件载入到java.library.path
(7)把JNI函数注册到系统
(8) 在java中调用native方法.
一 Java中声明JNI函数
在Java声明一个JNI函数很简单,就像在接口中声明函数一样,只是在前面多加个前缀Native.
比如:native String getStringFromJni();
:对于Java中声明JNI函数,JNI系统程序和应用程序开发都一样
二 编译java类成class文件,使用javah -jni java类名生成扩展名为h的头文件<wbr style="line-height:25px"><div style="line-height:25px"><span style="color:#003366; line-height:25px">如果你需要不同的JNI的C\C++实现文件的函数能互相调用,那么你就需要为你的JNI的C\C++端编写扩展名为h的头文件。</span></div> <div style="line-height:25px"><span style="color:#003366; line-height:25px">你可以手写头文件,也可以使用javah命令自动生成</span></div> <div style="line-height:25px"> <span style="color:#003366; line-height:25px">关于javah命令的详细内容请参考《</span><strong><a title="阅读全文" target="_blank" href="http://hubingforever.blog.163.com/blog/static/17104057920115218559186/" style="color:rgb(207,121,28); line-height:25px; text-decoration:none">javah命令详解</a></strong><span style="line-height:25px; color:rgb(0,51,102)">》</span> </div> <div style="line-height:25px"><span style="color:#000080; line-height:25px">对于JNI应用程序,一般不需要不同的JNI的C\C++实现文件的函数能互相调用。</span></div> <div style="line-height:25px"><span style="color:#000080; line-height:25px">但是对JNI系统程序,最好还是让不同的JNI的C\C++实现文件的函数能互相调用。</span></div> <div style="line-height:25px"> <span style="color:#000080; line-height:25px">如果我们是通过registerNativeMethods()手动把的JNI本地和相应java函数的对应关系注册到系统的,那么本地方法的命名似乎并没有严格的规定,否则</span><span style="line-height:25px; color:rgb(0,0,128)">本地方法的命名就必须严格按照规范来,必须为java_java类的包名_java类名_Java的函数名的形式,这里java类的包名用"_"代替"."。JNI系统程序一般都是采用前面一种方式,JNI应用程序则通常不用把JNI的C/C++函数和相应java函数的对应关系注册到系统。</span> </div> <div style="line-height:25px"> <span style="line-height:25px">比如</span><span style="color:#000080; line-height:25px">:</span>Hello.java文件</div> <div style="line-height:25px"> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">package com.robin;</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">public class Hello{</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px"><span style="line-height:23px; white-space:pre"></span>void sayHello()</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px"><span style="line-height:23px; white-space:pre"></span>{</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px"><span style="line-height:23px; white-space:pre"></span>System.out.println(getHelloStringFromJni());</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px"><span style="line-height:23px; white-space:pre"></span>}</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px"><span style="line-height:23px; white-space:pre"></span>native String getHelloStringFromJni();</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">}</span></div> </div> <div style="line-height:25px"><span style="font-size:13px; line-height:23px">JNI的本地方法就应该为:</span></div> <div style="line-height:25px"> <span style="font-size:13px; color:#0000ff; line-height:23px">JNIEXPORT jstring JNICALL</span><span style="font-size:13px; color:#ff6600; line-height:23px">Java</span><span style="font-size:13px; color:#0000ff; line-height:23px">_com_robin_Hello_</span><span style="font-size:13px; color:#ff6600; line-height:23px">getHelloStringFromJni(JNIEnv *, jobject);</span> </div> <div style="line-height:25px"><span style="line-height:25px">三 使用C/C++ 实现本地方法</span></div> <div style="line-height:25px"><span style="color:#003366; line-height:25px">接着可以开始在.c或.cpp文件中实现头文件声明的JNI函数。</span></div> <div style="line-height:25px"> <div style="line-height:25px"><span style="color:#000080; line-height:25px">JNI的所有的本地方法的第一个参数都是指向JNIEnv结构的。这个结构是用来调用JNI函数的。第二个参数jclass/jobject的意义,要看方法是不是静态的(static)或者实例(Instance)的。前者,jclass代表一个类对象的引用,而后者是被调用的方法所属对象的引用。 从第三个参数开始的才是java函数本身传递的参数。函数返回值和传入参数的类型都是根据约定映射java类型到本地C/C++类型。我们可以在JNI的C\C++本地方法对传入的java参数进行操作处理,处理完成可以返回空,也可以返回一个映射到C/C++的java类型。</span></div> </div> <div style="line-height:25px">关于JNI中参数的传递与操作的更多内容请参考《<strong><a title="阅读全文" target="_blank" href="http://hubingforever.blog.163.com/blog/static/17104057920115712234774/" style="color:rgb(207,121,28); line-height:25px; text-decoration:none">JNI中参数的传递与操作</a></strong>》</div> <div style="line-height:25px"> <span style="line-height:25px">注</span>:在C/C++中,如果一个函数没有进行声明,那么在定义它时,就同时进行声明了。</div> <div style="line-height:25px"><span style="line-height:25px">四编辑Android.mk</span></div> <div style="line-height:25px"> <div style="line-height:25px; font-weight:bold">示例1</div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">LOCAL_PATH := $(call my-dir)</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">include $(CLEAR_VARS)</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">LOCAL_MODULE := hello-jni</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">LOCAL_SRC_FILES := hello-jni.c</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">include $(BUILD_SHARED_LIBRARY)。</span></div> </div> <div style="line-height:25px">关于<span style="line-height:22px">Android.mk的详细内容请参照《</span><strong><a title="阅读全文" target="_blank" href="http://hubingforever.blog.163.com/blog/static/171040579201152185542166/" style="color:rgb(207,121,28); line-height:25px; text-decoration:none">Android.mk</a></strong>》</div> <div style="line-height:25px"><span style="line-height:25px">五编译JNI的C\C++源码生成.so文件</span></div> <div style="line-height:25px"> <span style="color:#003366; line-height:25px">对于JNI应用程序,编译JNI的C\C++源码是使用NDK来进行编译,具体请参照《</span><strong><a title="阅读全文" target="_blank" href="http://hubingforever.blog.163.com/blog/static/171040579201072743235305/" style="color:rgb(207,121,28); line-height:25px; text-decoration:none">JNI应用程序开发</a></strong><span style="line-height:25px; color:rgb(0,51,102)">》</span> </div> <div style="line-height:25px"> <span style="color:#003366; line-height:25px">对于JNI系统程序,你只要提供了</span><span style="color:#0000ff; line-height:25px">Android.mk</span><span style="color:#003366; line-height:25px">文件,在编译Android系统的时候,该Anroid.mk就会被执行,当然就进行编译并生成.so文件。</span> </div> <div style="line-height:25px"><span style="color:#003366; line-height:25px">一个.so文件就相当于一个JNI组件。</span></div> <div style="line-height:25px"> <span style="line-height:25px; color:rgb(0,51,102)">另外注意,</span><span style="color:#0000ff; line-height:25px">Android.mk</span><span style="line-height:25px; color:rgb(0,51,102)">中通过“</span><span style="color:#0000ff; line-height:25px">LOCAL_MODULE:= hello-jni</span><span style="line-height:25px; color:rgb(0,51,102)">”指定包名,此处包名为"hello-jni",生成的.so文件名为lib+包名+.so形成,这里将生成l</span><span style="color:#0000ff; line-height:25px">ibhello-jni.so</span><span style="line-height:25px; color:rgb(0,51,102)">文件</span> </div> <div style="line-height:25px"><span style="line-height:25px">六在Java中把.so文件载入到java.library.path</span></div> <div style="line-height:25px"> <div style="line-height:25px"> <span style="color:#003366; line-height:25px">在java代码中可以通过</span><span style="color:#0000ff; line-height:25px">System.loadLibrary</span><span style="color:#003366; line-height:25px">方法把.so文件载入java.library.path,以便我们使用。</span> </div> <div style="line-height:25px">比如</div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px"><span style="line-height:25px; white-space:pre"></span>static {</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px"> System.loadLibrary("hello-jni");</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px"> }</span></div> <div style="line-height:25px"> <span style="line-height:25px">注意</span>:System.loadLibrary的参数是包名hello-jni,而不文件名libhello-jni.so</div> </div> <div style="line-height:25px"> <div style="line-height:25px"> <span style="line-height:25px"><span style="color:#003366; line-height:25px">JNI_OnLoad()</span></span><span style="color:#000080; line-height:25px">与</span><span style="line-height:25px"><span style="color:#003366; line-height:25px">JNI_OnUnload()</span></span><span style="color:#000080; line-height:25px">函数</span> </div> <div style="line-height:25px"> <span style="line-height:25px; color:rgb(0,0,128)"> </span><span style="color:#003366; line-height:25px">当Android的VM(Virtual Machine)执行到</span><span style="color:#ff6600; line-height:25px">System.loadLibrary()</span><span style="color:#003366; line-height:25px">函数时,首先会去执行JNI组件(*.so)里的</span><span style="color:#ff6600; line-height:25px">JNI_OnLoad()</span><span style="color:#003366; line-height:25px">函数。</span> </div> <div style="line-height:25px"> <span style="color:#003366; line-height:25px">它的用途有二</span><span style="line-height:25px; color:rgb(0,0,128)">:</span> </div> <div style="line-height:25px"> <span style="line-height:25px; color:rgb(0,0,128)"> </span><span style="color:#000080; line-height:25px">1)告诉VM此C组件使用那一个JNI版本。如果你的在*.so没有提供</span><span style="color:#ff6600; line-height:25px">JNI_OnLoad()</span><span style="color:#000080; line-height:25px">函数,VM会默认该*.so档是使用最老的JNI 1.1版本。</span> </div> <div style="line-height:25px"> <span style="color:#000080; line-height:25px"> 由于新版的JNI做了许多扩充,如果你需要使用JNI的新版功能(例如JNI 1.4的java.nio.ByteBuffer),那么JNI_OnLoad()函数返回JNI_VERSION_1_4或以上。如果你返回</span><span style="color:#ff6600; line-height:25px">JNI_VERSION_1_4</span><span style="color:#000080; line-height:25px">的话,就是告知VM你使用的是</span><span style="color:#ff6600; line-height:25px">JNI_VERSION_1_4</span><span style="color:#000080; line-height:25px">版本的JNI。</span> </div> <div style="line-height:25px; color:rgb(0,0,128)"> 2)由于VM执行到System.loadLibrary()函数时,就会立即先呼叫JNI_OnLoad(), 所以C组件的开发者可以藉由JNI_OnLoad()来进行组件内的初期值之设定(Initialization) 。</div> <div style="line-height:25px; color:rgb(0,0,128)"> 例如,在Android的/system/lib/libmedia_jni.so档案里,就提供了JNI_OnLoad()函数,其程式码片段为:</div> <div style="line-height:25px; color:rgb(0,0,128)"><span style="line-height:25px">示例1</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">//#define LOG_NDEBUG 0</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">#define LOG_TAG "MediaPlayer-JNI"</span></div> <div style="line-height:25px"><span style="color:#0000ff; line-height:25px">jint JNI_OnLoad(JavaVM* vm, void* reserved)</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">{</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">JNIEnv* env = NULL;</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">jint result = -1;</span></div> <div style="line-height:25px"> <span style="color:#0000ff; line-height:25px">if (vm-&gt;GetEnv((void**) &amp;env, JNI_VERSION_1_4) != JNI_OK)</span><span style="color:#3366ff; line-height:25px">{</span> </div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">LOGE("ERROR: GetEnv failed\n");</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">goto bail;</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">}</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">assert(env != NULL);</span></div> <div style="line-height:25px"> <span style="color:#3366ff; line-height:25px">if (register_android_media_MediaPlayer(</span><span style="color:#0000ff; line-height:25px">env</span><span style="color:#3366ff; line-height:25px">) &lt; 0) {</span> </div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">LOGE("ERROR: MediaPlayer native registration failed\n");</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">goto bail;</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">}</span></div> <div style="line-height:25px"> <span style="color:#3366ff; line-height:25px">if (register_android_media_MediaRecorder(</span><span style="color:#0000ff; line-height:25px">env)</span><span style="color:#3366ff; line-height:25px">&lt; 0) {</span> </div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">LOGE("ERROR: MediaRecorder native registration failed\n");</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">goto bail;</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">}</span></div> <div style="line-height:25px"> <span style="color:#3366ff; line-height:25px">if (register_android_media_MediaScanner(</span><span style="color:#0000ff; line-height:25px">env</span><span style="color:#3366ff; line-height:25px">) &lt; 0) {</span> </div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">LOGE("ERROR: MediaScanner native registration failed\n");</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">goto bail;</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">}</span></div> <div style="line-height:25px"> <span style="color:#3366ff; line-height:25px">if (register_android_media_MediaMetadataRetriever(</span><span style="color:#0000ff; line-height:25px">env</span><span style="color:#3366ff; line-height:25px">) &lt; 0) {</span> </div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">LOGE("ERROR: MediaMetadataRetriever native registration failed\n");</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">goto bail;</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">}</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">/* success -- return valid version number */</span></div> <div style="line-height:25px"> <span style="color:#0000ff; line-height:25px">result = JNI_VERSION_1_4</span><span style="color:#3366ff; line-height:25px">;</span> </div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">bail:</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">return result;</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">}</span></div> <div style="line-height:25px"><span style="color:#000080; line-height:25px">此函数回传JNI_VERSION_1_4值给VM,于是VM知道了其所使用的JNI版本了。此外,它也做了一些初期的动作(该函数可呼叫任何本地函数),例如指令:</span></div> <div style="line-height:25px"><span style="color:#0000ff; line-height:25px">if (register_android_media_MediaPlayer(env) &lt; 0) {</span></div> <div style="line-height:25px"><span style="color:#0000ff; line-height:25px">LOGE("ERROR: MediaPlayer native registration failed\n");</span></div> <div style="line-height:25px"><span style="color:#0000ff; line-height:25px">goto bail;</span></div> <div style="line-height:25px"><span style="color:#0000ff; line-height:25px">}</span></div> <div style="line-height:25px">这里<span style="line-height:25px; color:rgb(0,0,255)">register_android_media_MediaPlayer(env)</span>将此组件提供的各个本地函数(Native Function)登记到VM里,以便能加快后续呼叫本地函数的效率。关于JNI本地函数的登记的详细内容请参考后文。</div> <div style="line-height:25px"> <span style="color:#003366; line-height:25px">当VM释放JNI组件时,则会调用</span><span style="color:#ff6600; line-height:25px">JNI_OnUnload()</span><span style="color:#003366; line-height:25px">函数,我们可以在此添加一些善后清除的代码。</span> </div> <div style="line-height:25px"> <span style="color:#000080; line-height:25px">当VM呼叫</span><span style="color:#ff6600; line-height:25px">JNI_OnLoad()</span><span style="color:#000080; line-height:25px">或</span><span style="color:#ff6600; line-height:25px">JNI_Unload()</span><span style="color:#000080; line-height:25px">函数时,都会将VM的指针(Pointer)传递给它们,其参数如下:</span> </div> <div style="line-height:25px"><span style="color:#0000ff; line-height:25px">jint JNI_OnLoad(JavaVM* vm, void* reserved) { }</span></div> <div style="line-height:25px"><span style="color:#0000ff; line-height:25px">jint JNI_OnUnload(JavaVM* vm, void* reserved){ }</span></div> <div style="line-height:25px"><span style="color:#000080; line-height:25px">在JNI_OnLoad()函数里,就必须透过VM来取得JNIEnv,并存入env变量里,如下述指令:</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">jint JNI_OnLoad(JavaVM* vm, void* reserved){</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">JNIEnv* env = NULL;</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">jint result = -1;</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">if (vm-&gt;GetEnv((void**) &amp;env, JNI_VERSION_1_4) != JNI_OK) {</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">LOGE("ERROR: GetEnv failed\n");</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">goto bail;</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">}</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">}</span></div> <div style="line-height:25px"><span style="color:#000080; line-height:25px">由于VM通常是多线程(Multi-threading)的执行环境。每一个线程在呼叫JNI_OnLoad()时,所传递进来的JNIEnv指标值都是不同的。</span></div> <div style="line-height:25px"><span style="color:#000080; line-height:25px">为了配合这种多执行绪的环境,JNI组件开发者在撰写本地函数时,也就可因JNIEnv不同而避免线程冲突问题,</span></div> <div style="line-height:25px"> <span style="color:#000080; line-height:25px">这样就能确保所写的本地函数能安全地在Android的多线程的VM里安全地执行。基于这个理由,当在呼叫JNI组件的本地函数时,都会将JNIEnv指标值传递给它。如</span><span style="line-height:25px">示例1,</span><span style="line-height:25px; color:rgb(0,0,128)">调用register_android_media_MediaPlayer(env)函数时,就将env指标值传递过去。如此,在register_android_media_MediaPlayer()函数就能根据JNIEnv的不同而避免线程的冲突。</span> </div> <div style="line-height:25px"><span style="color:#000080; line-height:25px">可以撰写下述指令:</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">if ((*env)-&gt;MonitorEnter(env, obj) == JNI_OK) {</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">//代码1</span></div> <div style="line-height:25px"> <span style="color:#3366ff; line-height:25px">}</span><span style="line-height:25px; color:rgb(51,102,255)">else {</span> </div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">//代码2</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">}</span></div> <div style="line-height:25px"> <span style="color:#000080; line-height:25px">该段代码用于查看是否已经有JNIEnv进入此obj,如果没有,此JNIEnv就进入该obj并返回JNI_OK,然后执行</span><span style="color:#0000ff; line-height:25px">代码1</span><span style="color:#000080; line-height:25px">;否则执行</span><span style="color:#0000ff; line-height:25px">代码2.</span> </div> <div style="line-height:25px"><span style="color:#000080; line-height:25px">还有,也可撰写下述指令:</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">if ((*env)-&gt;MonitorExit(env, obj) == JNI_OK) {</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">//代码1</span></div> <div style="line-height:25px"> <span style="color:#3366ff; line-height:25px">}</span><span style="line-height:25px; color:rgb(51,102,255)">else</span> </div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">{</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">//代码2</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">}</span></div> <div style="line-height:25px"> <span style="color:#000080; line-height:25px">该段代码用于查看该JNIEnv是否正在此obj,如果是该JNIEnv就离开此obj并返回JNI_OK,执行</span><span style="color:#0000ff; line-height:25px">代码1</span><span style="color:#000080; line-height:25px">,否则执行</span><span style="color:#0000ff; line-height:25px">代码2</span><span style="color:#000080; line-height:25px">。</span> </div> </div> <div style="line-height:25px"><span style="color:#000080; line-height:25px">另外注意对于本步骤,JNI应用程序开发和JNI系统程序开发没有什么不同。</span></div> <div style="line-height:25px"><span style="line-height:25px">七 把JNI函数注册到系统</span></div> <div style="line-height:25px"> <span style="color:#003366; line-height:25px"> 应用层级的Java类别通过VM而调用到本地函数,VM默认是按照JNI的java函数和C\C++本地函数的命名规范,去寻找*.so里的本地函数。查找的过程还是有点耗时</span> </div> <div style="line-height:25px"><span style="color:#003366; line-height:25px"> 如果调用次数很多,而每次都寻找一遍的话,会花掉许多时间。</span></div> <div style="line-height:25px"><span style="color:#003366; line-height:25px">如果你想节约VM查找JNI本地函数的时间,你可以通过registerNativeMethods()函数把JNI本地函数向VM进行登记,以节约VM查找JNI本地函数的时间。</span></div> <div style="line-height:25px"><span style="color:#003366; line-height:25px">如果你的JNI本地函数的命名没严格遵守java函数和C\C++本地函数对应的命名规范,你必须通过registerNativeMethods()函数把JNI本地函数向VM进行登记,以便VM能找到你的JNI本地函数</span></div> <div style="line-height:25px"><span style="color:#003366; line-height:25px">例如,在Android的/system/lib/libmedia_jni.so档案里的代码段如下:</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">//#define LOG_NDEBUG 0</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">#define LOG_TAG "MediaPlayer-JNI"</span></div> <div style="line-height:25px"> <span style="font-size:13px; color:#0000ff; line-height:23px">static JNINativeMethod gMethods[]</span><span style="font-size:13px; color:#3366ff; line-height:23px">= {</span> </div> <div style="line-height:25px"> <span style="font-size:13px; color:#3366ff; line-height:23px">{"</span><span style="font-size:13px; color:#99cc00; line-height:23px">setDataSource</span><span style="font-size:13px; color:#3366ff; line-height:23px">", "</span><span style="font-size:13px; color:#ff6600; line-height:23px">(Ljava/lang/String;)V</span><span style="font-size:13px; color:#3366ff; line-height:23px">",</span> </div> <div style="line-height:25px"> <span style="font-size:13px; color:#99cc00; line-height:23px">(void *)android_media_MediaPlayer_setDataSource</span><span style="font-size:13px; color:#3366ff; line-height:23px">},</span> </div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">{"setDataSource", "(Ljava/io/FileDescriptor;JJ)V",</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">(void *)android_media_MediaPlayer_setDataSourceFD},</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">{"prepare", "()V", (void *)android_media_MediaPlayer_prepare},</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">{"prepareAsync", "()V", (void *)android_media_MediaPlayer_prepareAsync},</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">{"_start", "()V", (void *)android_media_MediaPlayer_start},</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">{"_stop", "()V", (void *)android_media_MediaPlayer_stop},</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">{"getVideoWidth", "()I", (void *)android_media_MediaPlayer_getVideoWidth},</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">{"getVideoHeight", "()I", (void *)android_media_MediaPlayer_getVideoHeight},</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">{"seekTo", "(I)V", (void *)android_media_MediaPlayer_seekTo},</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">{"_pause", "()V", (void *)android_media_MediaPlayer_pause},</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">{"isPlaying", "()Z", (void *)android_media_MediaPlayer_isPlaying},</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">{"getCurrentPosition", "()I", (void *)android_media_MediaPlayer_getCurrentPosition},</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">{"getDuration", "()I", (void *)android_media_MediaPlayer_getDuration},</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">{"_release", "()V", (void *)android_media_MediaPlayer_release},</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">{"_reset", "()V", (void *)android_media_MediaPlayer_reset},</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">{"setAudioStreamType","(I)V", (void *)android_media_MediaPlayer_setAudioStreamType},</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">{"setLooping", "(Z)V", (void *)android_media_MediaPlayer_setLooping},</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">{"setVolume", "(FF)V", (void *)android_media_MediaPlayer_setVolume},</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">{"getFrameAt", "(I)Landroid/graphics/Bitmap;",</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">(void *)android_media_MediaPlayer_getFrameAt},</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">{"native_setup", "(Ljava/lang/Object;)V",</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">(void *)android_media_MediaPlayer_native_setup},</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">{"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize},</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">};</span></div> <div style="line-height:25px"> <span style="font-size:13px; color:#3366ff; line-height:23px"></span><span style="font-size:13px; color:#ff6600; line-height:23px">static int</span><span style="font-size:13px; color:#ff00ff; line-height:23px">register_android_media_MediaPlayer</span><span style="font-size:13px; color:#ff6600; line-height:23px">(JNIEnv *env)</span><span style="font-size:13px; color:#3366ff; line-height:23px">{</span> </div> <div style="line-height:25px"><span style="font-size:13px; color:#0000ff; line-height:23px">return AndroidRuntime::registerNativeMethods(env,</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#0000ff; line-height:23px">"android/media/MediaPlayer", gMethods, NELEM(gMethods));</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">}</span></div> <div style="line-height:25px"> <span style="font-size:13px; color:#3366ff; line-height:23px"></span><span style="font-size:13px; color:#ff6600; line-height:23px">jint</span><span style="font-size:13px; color:#ff00ff; line-height:23px">JNI_OnLoad</span><span style="font-size:13px; color:#ff6600; line-height:23px">(JavaVM* vm, void* reserved){</span> </div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">//省略</span></div> <div style="line-height:25px"> <span style="font-size:13px; color:#3366ff; line-height:23px">if (</span><span style="font-size:13px; color:#0000ff; line-height:23px">register_android_media_MediaPlayer(env) &lt; 0</span><span style="font-size:13px; color:#3366ff; line-height:23px">) {</span> </div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">LOGE("ERROR: MediaPlayer native registration failed\n");</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">goto bail;</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">}</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">//省略</span></div> <div style="line-height:25px"><span style="font-size:13px; color:#3366ff; line-height:23px">}</span></div> <div style="line-height:25px"> <span style="color:#000080; line-height:25px">当VM载入libmedia_jni.so档案时,就调用</span><span style="color:#0000ff; line-height:25px">JNI_OnLoad()</span><span style="color:#000080; line-height:25px">函数。接着,在</span><span style="color:#0000ff; line-height:25px">JNI_OnLoad()</span><span style="color:#000080; line-height:25px">中调用</span><span style="color:#0000ff; line-height:25px">register_android_media_MediaPlayer()</span><span style="color:#000080; line-height:25px">函数</span>。<span style="color:#000080; line-height:25px">然后,在</span><span style="color:#0000ff; line-height:25px">register_android_media_MediaPlayer()</span><span style="color:#000080; line-height:25px">函数中调用</span><span style="color:#0000ff; line-height:25px">AndroidRuntime::registerNativeMethods()</span>函数,</div> <div style="line-height:25px"> <span style="color:#000080; line-height:25px">向VM(即AndroidRuntime)登记</span><span style="color:#0000ff; line-height:25px">gMethods[]</span><span style="color:#000080; line-height:25px">数组所含的本地函数</span>。</div> <div style="line-height:25px"> <span style="color:#003366; line-height:25px">简而言之,registerNativeMethods()函数的用途有二</span>:</div> <div style="line-height:25px"> <span style="line-height:25px">1)</span><span style="color:#000080; line-height:25px">更有效率去找到函数</span>。</div> <div style="line-height:25px"> <span style="line-height:25px">2)<span style="color:#000080; line-height:25px"></span></span><span style="color:#000080; line-height:25px">可在执行期间进行抽换。由于</span><span style="color:#0000ff; line-height:25px">gMethods[]</span><span style="color:#000080; line-height:25px">是一个关于JNI的&lt;java函数,本地函数指针&gt;对照表,在程序执行时,可多次呼叫</span><span style="color:#0000ff; line-height:25px">registerNativeMethods()</span><span style="color:#000080; line-height:25px">函数来更换本地函数之指针,而达到弹性抽换本地函数之目的。</span> </div> <div style="line-height:25px"> <span style="color:#003366; line-height:25px">Andoird 中使用了一种不同传统Java JNI的方式来定义其native的函数。其中很重要的区别是Andorid使用了一种Java 和 C 函数的映射表数组,并在其中描述了函数的参数和返回值。这个数组的类型是</span><span style="color:#ff6600; line-height:25px">JNINativeMethod</span><span style="color:#003366; line-height:25px">,定义如下:</span> </div> <div style="line-height:25px"><span style="color:#0000ff; line-height:25px">typedef struct {</span></div> <div style="line-height:25px"> <span style="color:#0000ff; line-height:25px">const char* name;</span><span style="color:#ff6600; line-height:25px">/*Java中函数的名字*/</span> </div> <div style="line-height:25px"> <span style="color:#0000ff; line-height:25px">const char* signature;</span><span style="color:#ff6600; line-height:25px">/*用于描述函数的参数和返回值的字符*/</span> </div> <div style="line-height:25px"> <span style="color:#0000ff; line-height:25px">void* fnPtr;</span><span style="color:#ff6600; line-height:25px">/*本地函数指针,指向C函数*/</span> </div> <div style="line-height:25px"><span style="color:#0000ff; line-height:25px">} JNINativeMethod;</span></div> <div style="line-height:25px"><span style="color:#000080; line-height:25px">其中比较难以理解的是第二个参数,例如 "()V" ,"(II)V", "(Ljava/lang/String;Ljava/lang/String;)V". 实际上这些字符是与函数的参数类型一一对应的。"()" 中的字符表示参数,后面的则代表返回值。例如"()V" 就表示void Func(); "(II)V" 表示 void Func(int, int);</span></div> <div style="line-height:25px">关于此的详细内容请参照《<strong><a title="阅读全文" target="_blank" href="http://hubingforever.blog.163.com/blog/static/171040579201151722833782/" style="color:rgb(207,121,28); line-height:25px; text-decoration:none">JNI中java类型的简写</a></strong>》</div> <div style="line-height:25px"><span style="line-height:25px; color:rgb(0,0,128); font-family:Arial,Helvetica,simsun,u5b8bu4f53">另外还有需要指出的是对于本步骤,JNI应用程序开发和JNI系统程序开发没有什么不同。</span></div> <div style="line-height:25px"> <span style="line-height:25px; font-family:Arial,Helvetica,simsun,u5b8bu4f53"></span> <div style="line-height:25px"><span style="line-height:25px">八、在java中调用native方法.</span></div> <div style="line-height:25px; color:rgb(0,0,128)">无论是对于JNI应用程序开发,还是JNI系统程序开发,你可以在java中像调用普遍的java方法一样调用native方法</div> </div> </wbr>
分享到:
评论

相关推荐

    关于Java的JNI开发时使用cl编译器的说明

    说明:作为Java程序员,当与c程序员进行联合开发时会使用到JNI编程技术。此时要求Java程序员必须要会使用dll文档,以使用调试Java代码。这里介绍使用Windows OS自带的c/c++编译器(cl)怎样把.c文档编译成.exe和.dll...

    android系统原理及开发要点详解

     本书适应广大的读者群,力求在Android的系统移植、应用程序开发、系统框架改进方面给读者全面的支持。不同的读者在学习本书时,应该使用不同的方法。  Android初级开发者:在本书指引下阅读代码,搭建系统开发...

    android_jni_demo

    Android 开发中JNI的使用总结, JNI是Java Native Interface 的缩写,意为java本地接口, 使用JNI技术可以使得java语言与其它开发语言(如 C、C++ 和汇编语言)编写的应用程序或库进行相互操作。Android系统中的JNI运行...

    Android JNI开发入门之C/C++实现示例程序

    Android应用程序(APK)怎样通过JNI调用Native C/C++实现的共享库。Android系统的Java虚拟机为C和C++实现两套不同的API,本工程分别对其作出示例程序。并编译出libhelloworld.so/libhelloworldcpp.so共享库

    AndroidStudio_3.0版本JNI开发

    JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的...例如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少要保证本地代码能工作在任何Java 虚拟机环境

    Android系统jni编程和调用实例

    Android开发通过jni技术编写C语言方法并实现java调用C方法的实例。自己用C语言编写了一个动态链接库ndkfoo,并通过java调用动态库中的函数invokeNativeFunction,返回一个字符串,在android程序中显示出来。是初学...

    android系统原理及开发要点详解_韩超_梁泉 4

     本书适应广大的读者群,力求在Android的系统移植、应用程序开发、系统框架改进方面给读者全面的支持。不同的读者在学习本书时,应该使用不同的方法。  Android初级开发者:在本书指引下阅读代码,搭建系统开发...

    android系统原理及开发要点详解_韩超_梁泉 1

     本书适应广大的读者群,力求在Android的系统移植、应用程序开发、系统框架改进方面给读者全面的支持。不同的读者在学习本书时,应该使用不同的方法。  Android初级开发者:在本书指引下阅读代码,搭建系统开发...

    android 完全中文版 开发应用详解

    1.1.1 android开发系统的由来 1 1.1.2 移动电话系统开发模式 2 1.1.3 未来移动电话的功能及android的优势 4 1.2 android的开发工作 6 1.2.1 android移植开发 6 1.2.2 android应用开发 8 1.2.3 android系统开发 9 1.3...

    Android系统开发入门_胡章焱驱动的编写

    四、在Android系统中编写JNI方法在应用程序框架层提供Java接口访问硬件 22 五、在Android系统的应用程序框架层(Framework)增加硬件服务接口...........26 六、在Android系统中编写APP通过应用程序框架层访问硬件服务...

    老罗android开发视频教程全集百度网盘下载

    Android 是Google开发的基于Linux平台的开源手机操作系统。它包括操作系统、用户界面和应用程序—— 移动电话工作所需的全部软件,而且不存在任何以往阻碍移动产业创新的专有权障碍。 Android以Java为编程语言,使...

    嵌入式系统/ARM技术中的浅谈JNI技术在嵌入式软件开发中的应用

    嵌入式软件的基本体系结构包括嵌入式实时操作系统RTOS(RealTime operating Systerrl)、嵌入式设备驱动程序、嵌入式应用程序编程接口(中间件)和嵌入式应用程序。 现阶段,计算机应用的普及、互联网技术的实用以及...

    Android-jni-test for smart210-led

    这是我自己写的通过JNI调用Linux内核中的驱动程序来完成Android上层应用程序的代码,硬件开发平台式友善的smart210,在他家自带的系统上直接做的应用程序,希望对大家有用

    Android系统架构概述PPT

    应用程序层主要就是由四大组件Activity、Service、Broadcast Receiver和Content Provider构成,它们是应用开发的基础。这个PPT从一个通用的应用程序架构开始,概述Android系统的专用驱动、HAL、关键服务、Dalvik、...

    Android底层开发技术实战详解--内核、移植和驱动.(电子工业.王振丽).part1

    8.3 实现显示系统的驱动程序210 8.3.1 goldfish中的framebuffer驱动程序210 8.3.2 使用gralloc模块的驱动程序214 8.4 msm高通处理器中的显示驱动实现224 8.4.1 msm中的framebuffer驱动程序225 8.4.2 msm中...

    Android底层开发技术实战详解--内核、移植和驱动.(电子工业.王振丽).part2

    8.3 实现显示系统的驱动程序210 8.3.1 goldfish中的framebuffer驱动程序210 8.3.2 使用gralloc模块的驱动程序214 8.4 msm高通处理器中的显示驱动实现224 8.4.1 msm中的framebuffer驱动程序225 8.4.2 msm中...

    Android 初学中阶高阶书籍_集合打包2

    Android系统概述,Android系统的开发综述,Android的Linux内核与驱动程序,Android的底层库和程序,Android的JAVA虚拟机 和JAVA环境,Android的GUI系统,Android的Audio系统,Android的Video 输入输出系统,Android的多媒体...

    Android 初学中阶高阶书籍_集合打包3

    Android系统概述,Android系统的开发综述,Android的Linux内核与驱动程序,Android的底层库和程序,Android的JAVA虚拟机 和JAVA环境,Android的GUI系统,Android的Audio系统,Android的Video 输入输出系统,Android的多媒体...

    JNI技术手册 c/c++调用java

    三、 嵌入式开发应用(JNI小例子) 20 1、 新增一个基础类 22 2、 定义新类继承基础类 23 3、 编写调用类 23 4、 新增两个本地方法 24 5、 修改 RunMain 类 25 6、 新增一个方法处理java对象 26 7、 新增一个方法处理...

Global site tag (gtag.js) - Google Analytics