android jni的使用
2016-02-03 16:11
337 查看
android中的jni,可以方便java与本地native的代码互相访问,上一篇文章是java访问本地方法的介绍,上一篇的做法是老版本jdk1.4之前的格式,新版本jdk1.6之后的格式是使用映射,本篇就是使用映射来实现java与native互相访问的一个实例,就是自己的一个笔记。
抛开android环境,单纯java跟native的交互:http://blog.csdn.net/lin20044140410/article/details/50605663
一,首先,还是通过eclipse新建一个app,关键代码如下,
一个 NewHelloJni.java 这里声明了本地方法,和native中想要访问的java中的方法
二,在app的根目录,新建一个文件夹 newjni,
里面放的文件有:Android.mk,com_nativedemo_hellonativedemo_NewHelloJni.cpp(本地方法的实现类),内容如下:
Android.mk的内容:
com_nativedemo_hellonativedemo_NewHelloJni.cpp的内容:
从native访问java,提供了两种语法,分别是C语法和c++语法:
c语法: (*env)->CallVoidMethod(env,...);
c++语法:env->CallVoidMethod(jobject,..)
因为c语言不支持对象的概念,所以在c语法中把env作为第一个参数传入,类似于C++隐式参数this指针。
另外JNIEnv这个指针是线程私有的,每个线程都有一个副本。
这个函数中的JNI_OnLoad,在jvm加载动态库时会被调用,其中一个作用是通过返回值告诉虚拟机使用的jni版本,因为不同的版本提供的功能函数不一样,如果是默认返回了老的版本jni1.1,将无法使用新版本的功能。
然后这个方法里调用了注册本地方法的函数,jniRegisterNativeMethods(env, "com/nativedemo/hellonativedemo/NewHelloJni",sMethods, NELEM(sMethods));第一个参数是包含有需要本地实现方法的jave类,第二个参数是一个映射数组,,第三个参数是有多少个这样的映射,在JNINativeMethod sMethods[]这个数组里面,第一个元素是java中的方法,第二个是Signature签名,第三个是本地实现方法。
其中第二个参数描述了函数参数和返回值,如:
"(II)V" 括号中字符表示参数,括号外的字符表示返回值,这些字符表示方法:void fun(int ,int)
每个字符的对应关系:
字符Java类型C类型
V void void
Z jbooleanboolean
I jint int
J jlong long
D jdouble double
F jfloat float
B jbyte byte
C jchar char
S
jshort short
数组以"["开始,后面跟具体类型:
[I jintArrayint[]
上面是基本数据类型的表示,如果是类类型,以"L"开头,以";"结尾,中间部分是用"/"隔开的包和类名,其在C环境下对应的表示是jobject,例外的是String类,其在C中表示为jstring。
如:(Ljava/lang/String;J)Z,表示的方法是:boolean fun(String , long)
mainactivity.java中,有使用的代码,比较容易看懂,不做解释了。
最后说一下,如何把native层的异常抛到java层:
抛开android环境,单纯java跟native的交互:http://blog.csdn.net/lin20044140410/article/details/50605663
一,首先,还是通过eclipse新建一个app,关键代码如下,
一个MainActivity.java : package com.nativedemo.hellonativedemo; import android.app.Activity; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.widget.TextView; import android.util.Log; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); NewHelloJni mNewHelloJni = new NewHelloJni(); mNewHelloJni.displayNativeStr(); mNewHelloJni.printJaveStrFromNative(); int sum = mNewHelloJni.getAddSumFromNative(10000,500); String mStr = mNewHelloJni.getStrFromNative(); Log.d("Hellojni","jni native ,getAddSumFromNative= "+sum+",mStr="+mStr); } static { Log.d("hellonative","Loading JNI jni Library"); //这里加载库时,不需要指定后缀,linux下的库是.so, System.loadLibrary("newhellojni"); } 。。。。。。 }
一个 NewHelloJni.java 这里声明了本地方法,和native中想要访问的java中的方法
package com.nativedemo.hellonativedemo; import android.util.Log; public class NewHelloJni{ String fromNativeStr ="It is from jave!"; int mInt =20000; public NewHelloJni(){ fromNativeStr= "init..."; } void setJaveStrFromNative(String str){ fromNativeStr = str; } void setJaveintFromNative(int mI){ mInt = mI; } void printJaveStrFromNative(){ Log.d("hellojni","from native str:"+fromNativeStr+",mInt="+mInt+",but show in jave!"); } native void displayNativeStr(); //需要本地实现的方法,前面都有个native关键字 native String getStrFromNative(); native int getAddSumFromNative(int num1,int num2); }
二,在app的根目录,新建一个文件夹 newjni,
里面放的文件有:Android.mk,com_nativedemo_hellonativedemo_NewHelloJni.cpp(本地方法的实现类),内容如下:
Android.mk的内容:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_LDLIBS := -llog LOCAL_SRC_FILES := \ com_nativedemo_hellona 4000 tivedemo_NewHelloJni.cpp LOCAL_C_INCLUDES := \ $(JNI_H_INCLUDE) LOCAL_SHARED_LIBRARIES := \ libandroid_runtime \ libnativehelper \ libutils \ libcutils \ liblog #LOCAL_PRELINK_MODULE := false LOCAL_MULTILIB := 32 LOCAL_MODULE:= libnewhellojni LOCAL_MODULE_TAGS := optional include $(BUILD_SHARED_LIBRARY)
com_nativedemo_hellonativedemo_NewHelloJni.cpp的内容:
#include <jni.h> #include "JNIHelp.h" #include "utils/Log.h" #include <string.h> #include <pthread.h> #include "android_runtime/AndroidRuntime.h" #include "android_runtime/Log.h" namespace android{ jobject objGlobal = NULL; JNIEnv* envGlobal =NULL; jclass jniClass =NULL; jobject instanceObj =NULL; jmethodID constuctorId =0; jfieldID fidInt =0; jfieldID fidStr =0; jmethodID jMIdNoArgs =0; jmethodID jMIdIArgs =0; static void displayNativeStr(JNIEnv* env, jobject obj){ env = AndroidRuntime::getJNIEnv(); jstring jStrJave=NULL; const char* cStrJave = NULL; const char* str= "set native string in native"; jstring jStr = env->NewStringUTF(str);//实例化一个字符串 jniClass = env->FindClass("com/nativedemo/hellonativedemo/NewHelloJni"); jniClass = env->GetObjectClass(obj); constuctorId = env->GetMethodID(jniClass,"<init>","()V"); //获取构造函数id if(instanceObj == NULL){ instanceObj = env->NewObject(jniClass,constuctorId); } if(objGlobal == NULL){ //objGlobal = env->NewGlobalRef(instanceObj); //实例化一个全局的引用 objGlobal = env->NewGlobalRef(obj); } fidInt = env->GetFieldID(jniClass,"mInt","I"); //访问java中的属性 int javeInt = env->GetIntField(objGlobal,fidInt); env->SetIntField(objGlobal,fidInt,99990); fidStr = env->GetFieldID(jniClass,"fromNativeStr","Ljava/lang/String;");//访问java中的字符串属性 //jStrJave = (jstring)env->GetObjectField(objGlobal,fidStr); //cStrJave = env->GetStringUTFChars(jStrJave,NULL); //env->ReleaseStringUTFChars(jStrJave,cStrJave); env->SetObjectField(objGlobal,fidStr,jStr); jMIdNoArgs = env->GetMethodID(jniClass, "printJaveStrFromNative", "()V");//访问java中的方法 if(jMIdNoArgs !=0){ env->CallVoidMethod(objGlobal, jMIdNoArgs); } jMIdIArgs = env->GetMethodID(jniClass, "setJaveintFromNative", "(I)V"); ALOGD("native jni code,constuctorId=%d,fidInt=%d,javeInt=%s,,fidStr=%d,jStrJave=%s,jMIdJave=%d,jMIdIArgs=%d \n", constuctorId,fidInt,javeInt,fidStr,jStrJave,jMIdNoArgs,jMIdIArgs); if(jMIdIArgs!=0){ env->CallVoidMethod(objGlobal, jMIdIArgs,350000); } } //本地代码的实现,无参数的函数 static jstring getStrFromNative(JNIEnv* env, jobject obj){ const char* str= "from jni native string"; jstring jStr = env->NewStringUTF(str); return jStr; } //本地代码的实现,有参数的函数 static int getAddSumFromNative(JNIEnv* env, jobject obj,jint num1,jint num2){ return num1+num2; } //java中的方法,跟本地方法的映射,通过这个映射,native中的方法名就不用完全依照jni命名规范(Java_+包名+类名+接口名)写的那么长了,可以自主定义。 static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"displayNativeStr","()V",(void *) displayNativeStr}, {"getStrFromNative","()Ljava/lang/String;",(void *) getStrFromNative}, {"getAddSumFromNative","(II)I",(void *) getAddSumFromNative} }; int register_com_nativedemo_hellonativedemo_NewHelloJni(JNIEnv* env) { //这个注册是把jni调用接口,设置到包"com/nativedemo/hellonativedemo/NewHelloJni"中,这样java中的应用程序就可以调用。 return jniRegisterNativeMethods(env, "com/nativedemo/hellonativedemo/NewHelloJni", sMethods, NELEM(sMethods)); } }//end namespace android /* * JNI Initialization */ jint JNI_OnLoad(JavaVM *jvm, void *reserved) { JNIEnv *e; int status; ALOGV("Hello jni native : loading JNI\n"); // Check JNI version if (jvm->GetEnv((void **)&e, JNI_VERSION_1_6)) { ALOGE("JNI version mismatch error"); return JNI_ERR; } if ((status = android::register_com_nativedemo_hellonativedemo_NewHelloJni(e)) < 0) { ALOGE("jni hello native registration failure, status: %d", status); return JNI_ERR; } return JNI_VERSION_1_6; }
从native访问java,提供了两种语法,分别是C语法和c++语法:
c语法: (*env)->CallVoidMethod(env,...);
c++语法:env->CallVoidMethod(jobject,..)
因为c语言不支持对象的概念,所以在c语法中把env作为第一个参数传入,类似于C++隐式参数this指针。
另外JNIEnv这个指针是线程私有的,每个线程都有一个副本。
这个函数中的JNI_OnLoad,在jvm加载动态库时会被调用,其中一个作用是通过返回值告诉虚拟机使用的jni版本,因为不同的版本提供的功能函数不一样,如果是默认返回了老的版本jni1.1,将无法使用新版本的功能。
然后这个方法里调用了注册本地方法的函数,jniRegisterNativeMethods(env, "com/nativedemo/hellonativedemo/NewHelloJni",sMethods, NELEM(sMethods));第一个参数是包含有需要本地实现方法的jave类,第二个参数是一个映射数组,,第三个参数是有多少个这样的映射,在JNINativeMethod sMethods[]这个数组里面,第一个元素是java中的方法,第二个是Signature签名,第三个是本地实现方法。
其中第二个参数描述了函数参数和返回值,如:
"(II)V" 括号中字符表示参数,括号外的字符表示返回值,这些字符表示方法:void fun(int ,int)
每个字符的对应关系:
字符Java类型C类型
V void void
Z jbooleanboolean
I jint int
J jlong long
D jdouble double
F jfloat float
B jbyte byte
C jchar char
S
jshort short
数组以"["开始,后面跟具体类型:
[I jintArrayint[]
上面是基本数据类型的表示,如果是类类型,以"L"开头,以";"结尾,中间部分是用"/"隔开的包和类名,其在C环境下对应的表示是jobject,例外的是String类,其在C中表示为jstring。
如:(Ljava/lang/String;J)Z,表示的方法是:boolean fun(String , long)
mainactivity.java中,有使用的代码,比较容易看懂,不做解释了。
最后说一下,如何把native层的异常抛到java层:
jclass exceptionClass = env->FindClass("java/lang/RuntimeException"); env->ThrowNew(exceptionClass, "error msg!");异常的类型可以通过FindClass的参数指定,ThrowNew的第二个参数是异常的消息。
相关文章推荐
- android官方SearchView详细使用
- Android6.0没有权限读取外部存储的问题
- Android ViewTreeObserver 简介
- Android 特殊的单例Toast(防止重复显示)
- Android开发笔记(五十五)手机设备基本操作
- [读书笔记]《Android开发艺术探索》第二章笔记
- Android类似qq消息滑动菜单
- Source not found-Android 使用隐藏API(1)
- Android fragment解析2
- Android Studio的初步学习
- Android HandlerThread使用总结
- android shap 画图
- 生还是死?Android 进程优先级详解
- Android Fragment解析1
- Android Sqlite
- Android中Canvas绘图基础详解(附源码下载)
- android 时间选择器
- Android Studio 实用插件
- 优化 Android 线程和后台任务开发
- [Linphone Android]LinphoneService分析@onCreate