一、ndk调用java类示例
1,调用基本方法
1 /* 2 * Class: com_example_ndksample_MainActivity 3 * Method: ndkFindJavaClass 4 * Signature: ()Ljava/lang/String; 5 */ 6 /* 7 * ndk主动查找java类并调用. 8 */ 9 JNIEXPORT jstring JNICALL Java_com_example_ndksample_MainActivity_ndkFindJavaClass 10 (JNIEnv *env, jobject mainActivity) 11 { 12 jint age ; 13 jfieldID field; 14 jmethodID mid; 15 16 //1,找到java中相关的类, 17 //用全类名找,包名中的.改成/ 如com/example/ndksample/NdkCallJava 18 jclass cls = env->FindClass("com/example/ndksample/NdkCallJava"); 19 if (env->ExceptionCheck() ) { 20 return env->NewStringUTF("NdkCallJava not found \n"); 21 } 22 //2,在相关类中找到field 23 24 field = env->GetStaticFieldID(cls, "age", "I"); 25 if (env->ExceptionCheck() ) { 26 return env->NewStringUTF("age not found \n"); 27 } 28 //3,用field访问该属性 29 age = env->GetStaticIntField(cls,field); 30 31 //如果不是静态属性直接用env->GetIntField(jobject obj, jfieldID fieldID); 32 __android_log_print(ANDROID_LOG_ERROR, __func__,"age = %d\n", age); 33 34 //4,找到java 中的方法 35 /* 36 * 找到public void ageFromNdk(int ageFromNdk)后会调用失败,因为它是非静态的. 37 * 非静态成员要用jobject调用,同时这个对象没有从java中传来,所以这里不能正常调用, 38 * 只能用jclass调用静态的 public void ageFromNdkStatic(int ageFromNdk); 39 * 可以用env new一个类型为"com/example/ndksample/NdkCallJava"的jobject, 40 * 但这个 jobject只是ndk中的并不是java中的,用它调用了也不是所期待的那个. 41 */ 42 const char * method = "ageFromNdkStatic"; //它虽然是个private的,但可调用 43 mid = env->GetStaticMethodID(cls, method, "(I)V"); 44 if (env->ExceptionCheck() ) { 45 __android_log_print(ANDROID_LOG_ERROR, __func__,"%s not found\n",method); 46 return env->NewStringUTF("ageFromNdkStatic not found \n"); 47 } 48 //5,调用java中的静态方法 49 int newAge = 21;//这个是传给 ageFromNdkStatic(int ageFromNdk) 的参数 50 env->CallStaticVoidMethod(cls,mid,newAge); 51 age = env->GetStaticIntField(cls,field);//重新取age的值 52 53 //6,返回的结果 54 char result[64]={0}; 55 sprintf(result,"%s : %d\n","ndkFindJavaClass",age); 56 return env->NewStringUTF(result); 57 }
1 /* 2 * Class: com_example_ndksample_MainActivity 3 * Method: testClassAndMethod 4 * Signature: ()Ljava/lang/String; 5 */ 6 JNIEXPORT jstring JNICALL Java_com_example_ndksample_MainActivity_testClassAndMethod 7 (JNIEnv *env, jobject mainActivity) 8 { 9 /* 10 * 注意函数参数的传递及如何保存java函数返回值, 11 */ 12 jclass clazz ; 13 jobject javaObj; 14 jmethodID constructor; 15 jmethodID show ; 16 //1,构造对象第1步: 找到相关class 17 clazz = env->FindClass("com/example/ndksample/JavaSchool"); 18 if (env->ExceptionCheck() ) { 19 return env->NewStringUTF("find class exception \n"); 20 } 21 //2,构造对象第2步: 选定class中的一个构造函数 22 /* 23 * public JavaSchool(Context context) 24 * 所有类的构造函数名只能是<init> 25 */ 26 constructor = env->GetMethodID(clazz,"<init>","(Landroid/content/Context;)V"); 27 if (env->ExceptionCheck() ) { 28 return env->NewStringUTF("method <init> exception \n"); 29 } 30 //3,构造对象第3步: 为构造函数准备参数 Context context,,这里mainActivity就是 31 //4,构造对象第4步: env->NewObject(clazz,constructor,构造函数的参数)生成对象. 32 javaObj = env->NewObject(clazz,constructor,mainActivity);//mainActivity就是传过去的参数 33 //查找函数 public String show(int aID,String aName) 34 35 //5,找 public String show(int aID,String aName) 36 show = env->GetMethodID(clazz,"show","(ILjava/lang/String;)Ljava/lang/String;"); 37 if (env->ExceptionCheck() ) { 38 return env->NewStringUTF("show(String aName) not found \n"); 39 } 40 //6,构造传递的参数int id和String aName 41 int aID = 69; 42 43 //6.1,手动构造一个数组,为手动构造String准备 44 jbyteArray data = env->NewByteArray(8); 45 jbyte bytes [8] = "Lily"; 46 env->SetByteArrayRegion(data,0,8,bytes); 47 48 //6.2,手动构String 49 /* 50 * 其实可以简单一句就搞定,这里只是为了演示. 51 * 如: jstring aName = env->NewStringUTF("lucy"); 52 */ 53 /* 54 * 手动构String 第1步,找到 class 55 */ 56 jclass clz = env->FindClass("java/lang/String"); 57 if (env->ExceptionCheck() ) { 58 return env->NewStringUTF("java.lang.String not found \n"); 59 } 60 /* 61 * 手动构String 第2步,找到 一个构造函数, 62 * 这里选的是 public String(byte[] data); 63 * 注意所有类的构造函数(自写的,系统的)名字都只能是 <init>, 64 * env->GetMethodID(clz,"<init>","XXX"); 65 */ 66 jmethodID init = env->GetMethodID(clz,"<init>","([B)V"); 67 if (env->ExceptionCheck() ) { 68 return env->NewStringUTF("<init> exception \n"); 69 } 70 /* 71 * 手动构String 第3步,调用构造函数生成对象 72 * 注意构造函数的参数.要传过去.下面的data就是 73 */ 74 jobject stringObject = env->NewObject(clz,init,data); 75 76 //7,调用show函数,并获取public String show(int aID,String aName) 的返回值 77 jobject result = env->CallObjectMethod(javaObj,show,aID,stringObject); 78 79 return (jstring)result; 80 }
2,数组操作
1 /* 2 * Class: com_example_ndksample_MainActivity 3 * Method: testArrayAll 4 * Signature: ([B)Ljava/lang/String; 5 */ 6 JNIEXPORT void 7 JNICALL Java_com_example_ndksample_MainActivity_testArrayAll 8 (JNIEnv *env, jobject mainActivity, jbyteArray javaArray) 9 { 10 /* 11 * 一,jni操作数组的第1对api 访问数组全部数据. 12 * GetByteArrayElements要与ReleaseByteArrayElements成对出现.在它们之间对数组修改. 13 * ReleaseByteArrayElements的第三个参数有3个可选值.把副本中的内容更新到源数组,并释放副本. 14 */ 15 /* 16 * ReleaseByteArrayElements第三个参数mode的取值: 17 0 18 当isCopy是true时,用这个,说明jvm返回的是数组副本, 19 把副本的内容更新的源数组,并把副本释放. 20 JNI_COMMIT 21 copy back the content but do not free the elems buffer 22 当isCopy是false时,用这个,说明jvm返回的是数组引用, 23 这时不要释放数组. 24 JNI_ABORT 25 free the buffer without copying back the possible changes 26 这是只释放掉缓冲,不更新内容 27 */ 28 29 jboolean isCopy; 30 jint mode = JNI_ABORT;//若是拷贝,只释放副本. 31 32 //得到javaArray内容,有的jvm是拷贝一份副本,有的jvm是直接引用源数组.看isCopy的返回值. 33 jbyte *arrayCopy = env->GetByteArrayElements(javaArray,&isCopy); 34 jint len = env->GetArrayLength(javaArray); 35 if(isCopy){ 36 //在 拷贝源数组成副本的jvm上运行此代码 37 mode = 0; 38 }else{ 39 //在 引用源数组的jvm上运行此代码 40 mode = JNI_COMMIT; 41 } 42 //修改数组内容,若isCopy是true,那么修改的是副本,false,修改的是源数组. 43 for (int i = 1; i <= len ; ++ i) { 44 arrayCopy[i - 1] = len - i; 45 } 46 //将副本内容更新回java数组中. 47 env->ReleaseByteArrayElements(javaArray,arrayCopy,mode); 48 } 49 50 /* 51 * Class: com_example_ndksample_MainActivity 52 * Method: testArrayRegion 53 * Signature: ([B)Ljava/lang/String; 54 */ 55 JNIEXPORT void 56 JNICALL Java_com_example_ndksample_MainActivity_testArrayRegion 57 (JNIEnv *env, jobject mainActivity, jbyteArray javaArray) 58 { 59 /* 60 * 二,jni操作数组的第2对api 只拷贝数组中一段范围到目标buf中. 61 * 62 * SetByteArrayRegion与GetByteArrayRegion 63 * 64 */ 65 //1,准备缓冲,用来存放拷贝出来的一段副本. 66 jbyte *buf = new jbyte[16]; 67 //2,设定拷贝的范围区域 68 jint region = 5;//只对中间下标为[3,4,5,6,7]这5个感兴趣. 69 env->GetByteArrayRegion(javaArray,3,region,buf); 70 //3,修改这段区域副本. 71 for (jint i = 0; i < region; ++i) { 72 buf[i] = '-'; 73 } 74 //4,更新这段副本内容到源数组. 75 env->SetByteArrayRegion(javaArray,3,region,buf); 76 77 //5,删除存副本的缓冲区. 78 delete buf; 79 }
3, 使用STL
1 /* 2 * Class: com_example_ndksample_MainActivity 3 * Method: testSTL 4 * Signature: ()Ljava/lang/String; 5 */ 6 JNIEXPORT jstring JNICALL Java_com_example_ndksample_MainActivity_testSTL 7 (JNIEnv *env, jobject mainActivity) 8 { 9 /* 10 * test vector 11 */ 12 string result; 13 vector<int> v; 14 for (int i = 0; i < 16; ++i) { 15 v.push_back(i); 16 } 17 result.append("vector<int> :"); 18 for (int i = 0; i < v.size(); ++i) { 19 char b[3] = {'\0'}; 20 sprintf(b,"%d",v.at(i)); 21 result.append(b); 22 result.append(","); 23 } 24 result.append("\n"); 25 /* 26 * test map 27 */ 28 map<int,string> mp; 29 mp.insert(make_pair(0,"hell")); 30 mp.insert(make_pair(1,"lili")); 31 mp.insert(make_pair(2,"lucy")); 32 33 map<int,string>::iterator it; 34 result.append("map<int,string> :"); 35 for (it = mp.begin(); it != mp.end(); ++it) { 36 pair<int,string> p = *it; 37 char key[3] = {'\0'}; 38 sprintf(key,"%d",p.first); 39 result.append(key); 40 result.append(","); 41 result.append(p.second.c_str()); 42 result.append(" "); 43 } 44 result.append("\n"); 45 46 return env->NewStringUTF(result.c_str()); 47 }
4,NDK访问设备
1 /* 2 * Class: com_example_ndksample_MainActivity 3 * Method: ndkAccessDevGraphicsF0 4 * Signature: ()V 5 */ 6 JNIEXPORT void JNICALL 7 Java_com_example_ndksample_MainActivity_ndkAccessDevGraphicsF0( 8 JNIEnv *env, jobject mainActivity) 9 { 10 FILE * device; 11 device = fopen("/dev/graphics/fb0", "r"); 12 if (!device) { 13 __android_log_print(ANDROID_LOG_ERROR, __func__,"device open field \n"); 14 return; 15 } 16 FILE *fp; 17 fp = fopen("/sdcard/fb0data","w"); 18 if (!fp) { 19 __android_log_print(ANDROID_LOG_ERROR, __func__,"save file open field \n"); 20 return; 21 } 22 char ch ; 23 while((ch = fgetc(device) ) != EOF){ 24 fputc(ch,fp); 25 } 26 fclose(fp); 27 fclose(device); 28 }
二、准备java层的相关类.
JavaSchool.java
1 import android.content.Context;
2 import android.util.Log;
3 import android.widget.Toast; 4 5 public class JavaSchool { 6 private int id; 7 private String name; 8 private Context context; 9 10 //没有参数的. 11 public void show(){ 12 Log.e("JavaSchool", String.format("id = %d name = ,%s", id,name)); 13 } 14 public JavaSchool(){ 15 this.id = 0; 16 this.name = "default"; 17 } 18 //有参数的 19 public String show(int aID,String aName){ 20 this.id = aID; 21 this.name = aName; 22 MainActivity ma = (MainActivity) context; 23 String result = String.format("id = %d name = %s MainActivity.id = %d", id,name,ma.contextId); 24 Log.e("JavaSchool",result ); 25 Toast.makeText(context,result, Toast.LENGTH_LONG).show(); 26 return result; 27 } 28 public JavaSchool(Context context){ 29 this.context = context; 30 this.id = 0; 31 this.name = "default"; 32 } 33 }
NativeStudent.java
1 public class NativeStudent {
2 //ndk编程第2步,在java中声明方法. 3 public native String getName(); 4 public native static String getCls(); 5 public native int add(int x,int y); 6 }
NativeTeacher.java
1 public class NativeTeacher {
2 //ndk编程第2步,在java中声明方法. 3 public native String say(); 4 public native static String getName(); 5 public native int getAge(); 6 public native void setStudent(NativeStudent stu[]); 7 }
NdkCallJava.java
1 public class NdkCallJava {
2 //虽然它在java中是private,在NDK中一样访问 3 private static int age = 18; 4 private String name = "java"; 5 6 //static 注意它是private的 7 private static void ageFromNdkStatic(int ageFromNdk){ 8 age = ageFromNdk; 9 } 10 public static int getAgeStatic(){ 11 return age; 12 } 13 //normal 14 public void ageFromNdk(int ageFromNdk){ 15 age = ageFromNdk; 16 } 17 //注意它是private的 18 private void nameFromNdk(String name){ 19 this.name = name; 20 } 21 22 //下面两个函数只是为了调用 23 public native String ndkCallJavaField(); 24 25 public native String ndkCallJavaMethod(); 26 }
MainActivity.java
1 import android.app.Activity;
2 import android.os.Bundle;
3 import android.view.View; 4 import android.widget.TextView; 5 6 public class MainActivity extends Activity { 7 8 public int contextId = 100;//这个数据是在javaSchool中测试MainActivity是否传递成功 9 10 TextView output; 11 byte bytes[]; 12 13 //ndk编程第4步,load libNdkSample.so 它在Android.mk中指定的 14 /* 15 * LOCAL_MODULE := NdkSample 16 */ 17 static{ 18 System.loadLibrary("NdkSample"); 19 } 20 //下面是本地函数 21 public native String ndkFindJavaClass(); 22 public native void ndkAccessDevGraphicsF0(); 23 public native String testClassAndMethod(); 24 public native String testSTL(); 25 public native void testArrayAll(byte bytes[]); 26 public native void testArrayRegion(byte bytes[]); 27 28 public void onClickTestSTL(View btn){ 29 String ret = testSTL(); 30 output.append(ret + "\n"); 31 } 32 public void init(){ 33 bytes = new byte[16]; 34 for (byte i = 0; i < bytes.length; i++) { 35 bytes[i] = i; 36 } 37 } 38 public void onClickTestArrayRegion(View btn){ 39 output.append("被ndk修改前:"); 40 for (int i = 0; i < bytes.length; i++) { 41 output.append(bytes[i] + ","); 42 } 43 testArrayRegion(bytes); 44 output.append("\n被ndk修改后:"); 45 for (int i = 0; i < bytes.length; i++) { 46 output.append(bytes[i] + ","); 47 } 48 } 49 public void onClickTestArrayAll(View btn){ 50 bytes = new byte[16]; 51 for (byte i = 0; i < bytes.length; i++) { 52 bytes[i] = i; 53 } 54 output.append("被ndk修改前:"); 55 for (int i = 0; i < bytes.length; i++) { 56 output.append(bytes[i] + ","); 57 } 58 testArrayAll(bytes); 59 output.append("\n被ndk修改后:"); 60 for (int i = 0; i < bytes.length; i++) { 61 output.append(bytes[i] + ","); 62 } 63 } 64 public void onClickTestClassAndMethod(View btn){ 65 String ret = testClassAndMethod(); 66 output.append(ret + "\n"); 67 } 68 public void onClickAccessDevGraphicsF0(View btn){ 69 ndkAccessDevGraphicsF0(); 70 } 71 72 public void onClickNdkCallJavaField(View btn){ 73 NdkCallJava ncj = new NdkCallJava(); 74 String result = ncj.ndkCallJavaField(); 75 output.append(result) ; 76 } 77 public void onClickNdkFindJavaClass(View btn){ 78 String ret =