使用NDK移植开源项目,JNI的使用技巧

fmms 12年前
     <h2> jni 的介绍</h2>    <p> <span style="line-height:25px;background-color:#ffffff;font-family:arial, 宋体, sans-serif;">JNI 是Java Native Interface的缩写,中文为JAVA本地调用。从Java1.1开始,Java Native Interface(JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是 C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。以下介绍Android 中如何使用jni移植开源库的技巧.</span></p>    <h2> JNI日志输出到Logcat中</h2> #include <android/log.h>    <br /> #define LOG_TAG "===xcloud==="    <br /> #define LOGI(...)    <u>android_log_print(ANDROID_LOG_INFO,LOG_TAG,</u>VA_ARGS__)    <br /> #define LOGW(...)    <u>android_log_print(ANDROID_LOG_WARN,LOG_TAG,</u>VA_ARGS__)    <br /> #define LOGE(...)    <u>android_log_print(ANDROID_LOG_ERROR,LOG_TAG,</u>VA_ARGS__)    <br />    <p> </p>    <p>Android.mk文件添加编译模块:<br /> LOCAL_LDLIBS=-lm -llog<br /> 使用方法:<br /> LOGE("%s",test);</p>    <h2>JNI调用Java方法</h2> 以调用String 的getBytes方法为例:    <br /> 第一步:    <br /> jclass clsstring = (*env)->FindClass(env,"java/lang/String"); //找到Java String 类    <br /> 第二步:    <br /> jstring strencode = (*env)->NewStringUTF(env,"utf-8"); //得到一个jstring 对象    <br /> 第三步:    <br /> jmethodID mid = (*env)->GetMethodID(env,clsstring, "getBytes", "(Ljava/lang/String;)[B"); //得到getBytes方法    <br />    <p> </p>    <p>第四步:<br /> jbyteArray barr= (jbyteArray)(*env)->CallObjectMethod(env,jstr, mid, strencode); //“jstr 为传入的字符串”调用getBytes方法</p>    <p>第五步:<br /> jsize alen = (*env)->GetArrayLength(env,barr); //得到数组长度</p>    <p> </p>    <h2>jstring 转换 char*</h2> char* test=(char*)(*env)->GetStringUTFChars(env,jstringVariable,NULL);    <br />    <p> </p>    <p> </p>    <h2>返回一个java对象数组</h2> 第一步:    <br /> jclass objectClass=(*env)->FindClass(env,"com/xuzhitech/xcloud/resource"); //找到对应的java 类(对象)    <br /> 第二步:    <br /> jobjectArray array=(*env)->NewObjectArray(env,currentListCount,objectClass,NULL); //通过取到的java类(对象)创建一个指定固定大小的数组    <br /> 第三步:    <br /> jfieldID _uri=(*env)->GetFieldID(env,objectClass,"uri","Ljava/lang/String;");//找到对象中的列    <br /> 注意:在JNI中并未提供jstring 类型的对象,所以必须通过L指定包名找到该类,在有提供的类型中,可以直接使用该类型的大写首字母(jlong 需使用J)    <br /> 如jint 类型可以如此编写:    <br /> jfieldID _vcr=(*env)->GetFieldID(env,objectClass,"is_vcr","I");对应的JNI提供类型可以参考如下    <a title="http://download.oracle.com/javase/1.4.2/docs/guide/jni/spec/functions.html" href="/misc/goto?guid=4959518068407399825">http://download.oracle.com/javase/1.4.2/docs/guide/jni/spec/functions.html</a>    <br /> 第四步:    <br /> jmethodID jid = (*env)->GetMethodID(env,objectClass,"<init>","()V");//"<init>"代表可以访问默认构造函数    <br /> jobject jobj=(*env)->NewObject(env,objectClass,jid); //通过第一步找到的jclass创建对应的对象    <br />    <p> </p>    <p>第五步:<br /> (*env)->SetObjectField(env,jobj,_modtime,jmodtime); //为对象中的每一列赋值。注意:如果JNI有提供的数据类型,可按提供的类型为对象中的列赋值,<br /> 如:(*env)->SetIntField(env,jobj,_vcr,current->is_vcr);<br /> 第六步:<br /> (*env)->SetObjectArrayElement(env,array,i,jobj);将对象设置进第二步声明的数组中<br /> DeleteLocalRef(env,jobj);//删除引用对象</p>    <h2>在JNI中循环读取对象数组</h2>    <p> </p>    <div class="cnblogs_code">     <div>      <span style="color:#0000ff;">for</span> (i =       <span style="color:#800080;">0</span> ;i < currentListCount ;i++) {      <br /> jobject obj = (*env)->GetObjectArrayElement(env,array,i);      <br /> jstring jstr=(*env)->GetObjectField(env,obj,_uri);      <br /> LOGE(      <span style="color:#800000;">"</span>      <span style="color:#800000;">=====uri===%s====</span>      <span style="color:#800000;">"</span>,jstringtoChar(env,jstr));      <br /> }      <br /> 上面使用到的jstringtoChar方法代码:      <br />      <span style="color:#008000;">/*</span>      <span style="color:#008000;">*<br /> *jstring to char<br /> </span>      <span style="color:#008000;">*/</span>      <br />      <span style="color:#0000ff;">char</span>* jstringtoChar(JNIEnv* env,jstring jstr){      <br />      <span style="color:#0000ff;">char</span>* rtn = NULL;      <br /> jclass clsstring = (*env)->FindClass(env,      <span style="color:#800000;">"</span>      <span style="color:#800000;">java/lang/String</span>      <span style="color:#800000;">"</span>);      <br /> jstring strencode = (*env)->NewStringUTF(env,      <span style="color:#800000;">"</span>      <span style="color:#800000;">utf-8</span>      <span style="color:#800000;">"</span>);      <br /> jmethodID mid = (*env)->GetMethodID(env,clsstring,       <span style="color:#800000;">"</span>      <span style="color:#800000;">getBytes</span>      <span style="color:#800000;">"</span>,       <span style="color:#800000;">"</span>      <span style="color:#800000;">(Ljava/lang/String;)[B</span>      <span style="color:#800000;">"</span>);      <br /> jbyteArray barr= (jbyteArray)(*env)->CallObjectMethod(env,jstr, mid, strencode);      <br /> jsize alen = (*env)->GetArrayLength(env,barr);      <br /> jbyte* ba = (*env)->GetByteArrayElements(env,barr, JNI_FALSE);      <br />      <span style="color:#0000ff;">if</span> (alen >       <span style="color:#800080;">0</span>)      <br /> {      <br /> rtn = (      <span style="color:#0000ff;">char</span>*)malloc(alen +       <span style="color:#800080;">1</span>);      <br /> memcpy(rtn, ba, alen);      <br /> rtn[alen] =       <span style="color:#800080;">0</span>;      <br /> }      <br /> (*env)->ReleaseByteArrayElements(env,barr, ba,       <span style="color:#800080;">0</span>);      <br />      <span style="color:#0000ff;">return</span> rtn;     </div>    </div>    <p>} </p>    <p> </p>    <h2>cpp JNI与 c JNI的主要注意事项:</h2> 用C编写jni文件,访问JNI内置提供方法格式:    <br /> (*env)->SetObjectArrayElement(env,array,i,jobj);    <br /> 用CPP编写JNI文件,访问JNI内置提供方法格式:    <br /> env->SetObjectArrayElement(array,i,jobj);    <br />    <span style="color:red;">另外,使用CPP编写的JNI代码,在调用C语言编写的库的时候,要添加以下代码,才可以正常使用(不然在链接的时候找不到相关接口:undefined reference.....):</span>    <br />    <span style="color:red;">ifdef __cplusplus</span>    <br />    <span style="color:red;">extern "C" {</span>    <br />    <span style="color:red;">#endif</span>    <br />    <span style="color:red;">... //引入的头文件</span>    <br />    <span style="color:red;">#ifdef __cplusplus</span>    <br />    <span style="color:red;">}</span>    <br />    <span style="color:red;">#endif</span>    <br />    <p> </p>    <p>其他用法几乎一致。</p>    <p> </p>    <h2>JNI 使用Native注册</h2> 按照上面的方法写函数体,必须遵循JNI官方的一大堆标准进行方法的定义,有时候方法一多,不大好管理,也不利用查看,并且每次都要写一大堆恶心的标准方法名也不是一件好事。对此JNI有一套可以通过Native 注册的机制可以使用,以方便函数体的编写。    <br />    <p> </p>    <p>以下是Android 调用JNI注册Natives 的步骤:<br /> 第一步<br /> 声明Java中要调用jni 的类路径:<br /> static const char *className="com/xuzhitech/xcloud/cadaver";<br /> 第二步<br /> 创建方法格式结构体:<br /> struct JNINativeMethod {<br /> const char* name;//method name <br /> const char* signature; //java method return value <br /> void* fnPtr;//c/c++ method <br /> } ;</p>    <p>第三步<br /> 使用结构体注册需要供Android 调用的方法体:</p>    <div class="cnblogs_code">     <div>      <span style="color:#0000ff;">static</span> JNINativeMethod methods[] = {      <br /> {      <span style="color:#800000;">"</span>      <span style="color:#800000;">StringTestOne</span>      <span style="color:#800000;">"</span>,       <span style="color:#800000;">"</span>      <span style="color:#800000;">()Ljava/lang/String;</span>      <span style="color:#800000;">"</span>, (      <span style="color:#0000ff;">void</span>*)StringTestOne},      <br /> {      <span style="color:#800000;">"</span>      <span style="color:#800000;">executels</span>      <span style="color:#800000;">"</span>,      <span style="color:#800000;">"</span>      <span style="color:#800000;">()[Lcom/xuzhitech/xcloud/resource;</span>      <span style="color:#800000;">"</span>,(      <span style="color:#0000ff;">void</span>*)executels},      <br /> {      <span style="color:#800000;">"</span>      <span style="color:#800000;">getProgress</span>      <span style="color:#800000;">"</span>,      <span style="color:#800000;">"</span>      <span style="color:#800000;">()Lcom/xuzhitech/xcloud/fileProgress;</span>      <span style="color:#800000;">"</span>,(      <span style="color:#0000ff;">void</span>*)getProgress},      <br /> {      <span style="color:#800000;">"</span>      <span style="color:#800000;">getdownloadState</span>      <span style="color:#800000;">"</span>,      <span style="color:#800000;">"</span>      <span style="color:#800000;">()I</span>      <span style="color:#800000;">"</span>,(      <span style="color:#0000ff;">void</span>*)getdownloadState},      <br /> {      <span style="color:#800000;">"</span>      <span style="color:#800000;">changeExecutable</span>      <span style="color:#800000;">"</span>,      <span style="color:#800000;">"</span>      <span style="color:#800000;">(Ljava/lang/String;Ljava/lang/String;)I</span>      <span style="color:#800000;">"</span>,(      <span style="color:#0000ff;">void</span>*)changeExecutable},      <br /> {      <span style="color:#800000;">"</span>      <span style="color:#800000;">Login</span>      <span style="color:#800000;">"</span>,      <span style="color:#800000;">"</span>      <span style="color:#800000;">(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I</span>      <span style="color:#800000;">"</span>,(      <span style="color:#0000ff;">void</span>*)Login},      <br /> {      <span style="color:#800000;">"</span>      <span style="color:#800000;">getCurrentListCount</span>      <span style="color:#800000;">"</span>,      <span style="color:#800000;">"</span>      <span style="color:#800000;">()I</span>      <span style="color:#800000;">"</span>,(      <span style="color:#0000ff;">void</span>*)getCurrentListCount},      <br /> {      <span style="color:#800000;">"</span>      <span style="color:#800000;">mkcol</span>      <span style="color:#800000;">"</span>,      <span style="color:#800000;">"</span>      <span style="color:#800000;">(Ljava/lang/String;)I</span>      <span style="color:#800000;">"</span>,(      <span style="color:#0000ff;">void</span>*)mkcol},      <br /> {      <span style="color:#800000;">"</span>      <span style="color:#800000;">deleteFile</span>      <span style="color:#800000;">"</span>,      <span style="color:#800000;">"</span>      <span style="color:#800000;">(Ljava/lang/String;)I</span>      <span style="color:#800000;">"</span>,(      <span style="color:#0000ff;">void</span>*)deleteFile},      <br /> {      <span style="color:#800000;">"</span>      <span style="color:#800000;">deleteCol</span>      <span style="color:#800000;">"</span>,      <span style="color:#800000;">"</span>      <span style="color:#800000;">(Ljava/lang/String;)I</span>      <span style="color:#800000;">"</span>,(      <span style="color:#0000ff;">void</span>*)deleteCol},      <br /> {      <span style="color:#800000;">"</span>      <span style="color:#800000;">getFullUri</span>      <span style="color:#800000;">"</span>,      <span style="color:#800000;">"</span>      <span style="color:#800000;">()Ljava/lang/String;</span>      <span style="color:#800000;">"</span>,(      <span style="color:#0000ff;">void</span>*)getFullUri},      <br /> {      <span style="color:#800000;">"</span>      <span style="color:#800000;">cdCommand</span>      <span style="color:#800000;">"</span>,      <span style="color:#800000;">"</span>      <span style="color:#800000;">(Ljava/lang/String;)I</span>      <span style="color:#800000;">"</span>,(      <span style="color:#0000ff;">void</span>*)cdCommand},      <br /> {      <span style="color:#800000;">"</span>      <span style="color:#800000;">logout</span>      <span style="color:#800000;">"</span>,      <span style="color:#800000;">"</span>      <span style="color:#800000;">()V</span>      <span style="color:#800000;">"</span>,(      <span style="color:#0000ff;">void</span>*)logout},      <br /> {      <span style="color:#800000;">"</span>      <span style="color:#800000;">doCopy</span>      <span style="color:#800000;">"</span>,      <span style="color:#800000;">"</span>      <span style="color:#800000;">(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I</span>      <span style="color:#800000;">"</span>,(      <span style="color:#0000ff;">void</span>*)doCopy},      <br /> {      <span style="color:#800000;">"</span>      <span style="color:#800000;">doMove</span>      <span style="color:#800000;">"</span>,      <span style="color:#800000;">"</span>      <span style="color:#800000;">(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I</span>      <span style="color:#800000;">"</span>,(      <span style="color:#0000ff;">void</span>*)doMove},      <br /> {      <span style="color:#800000;">"</span>      <span style="color:#800000;">getFile</span>      <span style="color:#800000;">"</span>,      <span style="color:#800000;">"</span>      <span style="color:#800000;">(Ljava/lang/String;Ljava/lang/String;)V</span>      <span style="color:#800000;">"</span>,(      <span style="color:#0000ff;">void</span>*)getFile},      <br /> {      <span style="color:#800000;">"</span>      <span style="color:#800000;">putFile</span>      <span style="color:#800000;">"</span>,      <span style="color:#800000;">"</span>      <span style="color:#800000;">(Ljava/lang/String;Ljava/lang/String;)V</span>      <span style="color:#800000;">"</span>,(      <span style="color:#0000ff;">void</span>*)putFile},      <br /> {      <span style="color:#800000;">"</span>      <span style="color:#800000;">lock</span>      <span style="color:#800000;">"</span>,      <span style="color:#800000;">"</span>      <span style="color:#800000;">(Ljava/lang/String;)I</span>      <span style="color:#800000;">"</span>,(      <span style="color:#0000ff;">void</span>*)      <span style="color:#0000ff;">lock</span>},      <br /> {      <span style="color:#800000;">"</span>      <span style="color:#800000;">unlock</span>      <span style="color:#800000;">"</span>,      <span style="color:#800000;">"</span>      <span style="color:#800000;">(Ljava/lang/String;Ljava/lang/String;)I</span>      <span style="color:#800000;">"</span>,(      <span style="color:#0000ff;">void</span>*)unlock},      <br /> {      <span style="color:#800000;">"</span>      <span style="color:#800000;">getToken</span>      <span style="color:#800000;">"</span>,      <span style="color:#800000;">"</span>      <span style="color:#800000;">(Ljava/lang/String;)Ljava/lang/String;</span>      <span style="color:#800000;">"</span>,(      <span style="color:#0000ff;">void</span>*)getToken},      <br /> {      <span style="color:#800000;">"</span>      <span style="color:#800000;">isSetToken</span>      <span style="color:#800000;">"</span>,      <span style="color:#800000;">"</span>      <span style="color:#800000;">(Ljava/lang/String;)I</span>      <span style="color:#800000;">"</span>,(      <span style="color:#0000ff;">void</span>*)isSetToken},     </div>    </div>    <p>}; </p>    <p>第一个参数为:Java实现要调用的方法名称<br /> 第二个参数为:该方法需要的返回值与方法参数,如->(方法参数)返回值,详细使用参考如下:<br /> 具体的每一个字符的对应关系如下</p>    <p>字符 Java类型 C类型</p>    <div class="cnblogs_code">     <div>      V       <span style="color:#0000ff;">void</span>       <span style="color:#0000ff;">void</span>      <br /> Z jboolean boolean      <br /> I jint       <span style="color:#0000ff;">int</span>      <br /> J jlong       <span style="color:#0000ff;">long</span>      <br /> D jdouble       <span style="color:#0000ff;">double</span>      <br /> F jfloat       <span style="color:#0000ff;">float</span>      <br /> B jbyte       <span style="color:#0000ff;">byte</span>      <br /> C jchar       <span style="color:#0000ff;">char</span>      <br /> S jshort       <span style="color:#0000ff;">short</span>     </div>    </div>    <p> </p>    <p>数组则以"["开始,用两个字符表示</p>    <p></p>    <div class="cnblogs_code">     <div>      [I jintArray       <span style="color:#0000ff;">int</span>[]      <br /> [F jfloatArray       <span style="color:#0000ff;">float</span>[]      <br /> [B jbyteArray       <span style="color:#0000ff;">byte</span>[]      <br /> [C jcharArray       <span style="color:#0000ff;">char</span>[]      <br /> [S jshortArray       <span style="color:#0000ff;">short</span>[]      <br /> [D jdoubleArray       <span style="color:#0000ff;">double</span>[]      <br /> [J jlongArray       <span style="color:#0000ff;">long</span>[]      <br /> [Z jbooleanArray boolean[]     </div>    </div> 也可返回任意java对象,如上代码的()[Lcom/xuzhitech/xcloud/resource;代表一个不带参数的方法,并且返回java 类中的Lcom/xuzhitech/xcloud/resource;数组。'['代表数组的意思,'L'代表类的意思    <br />    <p> </p>    <p>第三个参数为,第一个参数需要映射的本地c/c++对应的函数指针方法。</p>    <p>第四步<br /> 在加载jni的时候指定JNI版本并且通过传入进来的class路径注册Natives 方法</p>    <p> </p>    <div class="cnblogs_code">     <div>      <span style="line-height:21px;font-family:verdana, 'courier new';font-size:14px;">jint JNI_OnLoad(JavaVM* vm, </span>      <span style="line-height:21px;font-family:verdana, 'courier new';color:#0000ff;font-size:14px;">void</span>      <span style="line-height:21px;font-family:verdana, 'courier new';font-size:14px;">* reserved){</span>      </div>     <p>jint result = JNI_ERR;<br /> JNIEnv* env = NULL;<br /> jclass clazz;<br /> <span style="color:#0000ff;">int</span> methodsLenght;<br /> <span style="color:#0000ff;">if</span> ((*vm)->GetEnv(vm, (<span style="color:#0000ff;">void</span>**) &env, JNI_VERSION_1_4) != JNI_OK) {<br /> LOGE(<span style="color:#800000;">"</span><span style="color:#800000;">ERROR: GetEnv failed\n</span><span style="color:#800000;">"</span>);<br /> <span style="color:#0000ff;">return</span> JNI_ERR;<br /> }<br /> <span style="color:#008000;">//</span><span style="color:#008000;"> assert(env != NULL); </span><span style="color:#008000;"><br /> </span>clazz = (*env)->FindClass(env,className);<br /> <span style="color:#0000ff;">if</span> (clazz == NULL) {<br /> LOGE(<span style="color:#800000;">"</span><span style="color:#800000;">Native registration unable to find class '%s'</span><span style="color:#800000;">"</span>, className);<br /> <span style="color:#0000ff;">return</span> JNI_ERR;<br /> }<br /> methodsLenght = <span style="color:#0000ff;">sizeof</span>(methods) / <span style="color:#0000ff;">sizeof</span>(methods[<span style="color:#800080;">0</span>]);<br /> <span style="color:#0000ff;">if</span> ((*env)->RegisterNatives(env,clazz, methods, methodsLenght) < <span style="color:#800080;">0</span>) {<br /> LOGE(<span style="color:#800000;">"</span><span style="color:#800000;">RegisterNatives failed for '%s'</span><span style="color:#800000;">"</span>, className);<br /> <span style="color:#0000ff;">return</span> JNI_ERR;<br /> }<br /> <span style="color:#008000;">//</span><span style="color:#008000;"> </span><span style="color:#008000;"><br /> </span>result = JNI_VERSION_1_4;<br /> <span style="color:#0000ff;">return</span> result;<br /> }</p>    </div>    <p> </p>    <p> </p>    <p>注意,使用Natives注册运行程序时,它会先检测jni与java类使用jni 类的native 方法是否相对应与一致,即使你没有使用到该 方法也会进行检测。<br /> 第五步<br /> 通过上面的修改,c/c++编写的jni 文件就可以不带一大串标准名称了,您可以像正常编写c一样编写你的jni函数,如下:</p>    <div class="cnblogs_code">     <div>      <span style="color:#008000;">/*</span>      <span style="color:#008000;"><br /> *move file<br /> </span>      <span style="color:#008000;">*/</span>      <br /> jint doMove(JNIEnv* env,jobject thiz,jstring jsrc,jstring jdest,jstring jrename){      <br />      <span style="color:#0000ff;">char</span>* src=(      <span style="color:#0000ff;">char</span>*)(*env)->GetStringUTFChars(env,jsrc,NULL);      <br />      <span style="color:#0000ff;">char</span>* dest=(      <span style="color:#0000ff;">char</span>*)(*env)->GetStringUTFChars(env,jdest,NULL);      <br />      <span style="color:#0000ff;">char</span>* rename=(      <span style="color:#0000ff;">char</span>*)(*env)->GetStringUTFChars(env,jrename,NULL);      <br />      <span style="color:#0000ff;">int</span> returnValue=multi_move(src,dest,rename);      <br />      <span style="color:#008000;">//</span>      <span style="color:#008000;"> free(src);</span>      <span style="color:#008000;"><br /> </span>(*env)->ReleaseStringUTFChars(env,jsrc,src);      <br />      <span style="color:#008000;">//</span>      <span style="color:#008000;"> free(dest);</span>      <span style="color:#008000;"><br /> </span>(*env)->ReleaseStringUTFChars(env,jdest,dest);      <br />      <span style="color:#008000;">//</span>      <span style="color:#008000;"> free(rename);</span>      <span style="color:#008000;"><br /> </span>(*env)->ReleaseStringUTFChars(env,jrename,rename);      <br />      <span style="color:#0000ff;">return</span> returnValue;      <br /> }     </div>    </div>    <p> </p>    <p> 注:</p>    <p>JNI的一些基本方法的使用都可以参考这个网站:http://docs.oracle.com/javase/1.4.2/docs/guide/jni/spec/functions.html</p>    <p> </p>    <p>希望对你有帮助. <br /> <br /> 转自:<a href="/misc/goto?guid=4959518068491710849" target="_blank">http://www.cnblogs.com/TerryBlog/archive/2012/02/07/2340902.html</a></p>