一、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 = "default";
17 }
18 //有参数的
19 public String show(int aID,String aName){
20 this.id = aID;
21 = aName;
22 MainActivity ma = (MainActivity) context;
23 String result = String.format("id = %d name = %s = %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 = "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 = name;
20 }
21
22 //下面两个函数只是为了调用
23 public native String ndkCallJavaField();
24
25 public native String ndkCallJavaMethod();
26 }
MainActivity.java
1 import .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 它在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 =