简单JNI使用demo

android中使用JNI的小例子,直接上代码。

首先是Java类JniClient,定义native方法,User实体类就不上代码了,就简单定义了三个属性,name、age、sex。


 1 package com.example.ndkdemo;
2
3 public class JniClient {
4
5 /
6 通过JNI简单输出字符串
7 @return
8 */
9 static public native String printStr();
10
11 /
12 通过JNI简单进行整形加法操作
13 @param a
14 @param b
15 @return
16 /
17 static public native int addInt(int a, int b);
18
19 /**
20 通过JNI输入JAVA对象信息
21 @param user
22 @return
23 /
24 static public native String printUser(User user);
25
26 /**
27 通过JNI创建java对象
28 @param name
29 @param age
30 @param sex
31 @return
32 /
33 static public native User newUser(String name, int age, String sex);
34
35 /**
36 通过JNI调用无参构造函数创建java对象并且设置相应属性值
37 @param name
38 @param age
39 @param sex
40 @return
41 */
42 static public native User newUserNoArgs(String name, int age, String sex);
43 }

 

然后使用cmd进入工程的classes目录通过javah命令生成c代码的头文件,命令:javah com.example.ndkdemo.JniClient ,这里生成的文件名字为:com_example_ndkdemo_JniClient.h

在工程下面新建一个jni目录,将生成的头文件拷贝到jni目录下面,创建com_example_ndkdemo_JniClient.c文件,在com_example_ndkdemo_JniClient.c文件里面实现各个native方法,代码如下:


  1 #include com_example_ndkdemo_JniClient.h
2 #include
3 #include
4
5 // 引入log头文件
6 #include
7 // log标签
8 #define TAG “jniCLient”
9 // 定义info信息
10 #define LOGI(…) android_log_print(ANDROID_LOG_INFO,TAG,VA_ARGS)
11 // 定义debug信息
12 #define LOGD(…)
android_log_print(ANDROID_LOG_DEBUG, TAG, VA_ARGS)
13 // 定义error信息
14 #define LOGE(…) android_log_print(ANDROID_LOG_ERROR,TAG,VA_ARGS)
15
16
17 #ifdef
cplusplus
18 extern C
19 {
20 #endif
21 /
22 Class: com_example_ndkdemo_JniClient
23 Method: printStr
24 Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
25 /
26 JNIEXPORT jstring JNICALL Java_com_example_ndkdemo_JniClient_printStr
27 (JNIEnv env, jclass arg)
28 {
29 jstring str = (env)->NewStringUTF(env, HelloWorld from JNI !);
30 LOGI(log from jni);
31 return str;
32 }
33
34 /

35 Class: com_example_ndkdemo_JniClient
36 Method: AddInt
37 Signature: (II)I
38 /
39 JNIEXPORT jint JNICALL Java_com_example_ndkdemo_JniClient_addInt
40 (JNIEnv env, jclass arg, jint a, jint b)
41 {
42 return a + b;
43 }
44
45 /

46 printUser
47 jclass arg:因为方法为static,所以需要传入jclass参数,表明是哪个类的方法
48 /
49 /
50 1)如果是C++代码,则用(env),如果是C代码,则用env,否则报错: request for member ‘GetObjectClass’ in something not a structure or union
51 2)所有方法都加了一个参数env,否则报错:too few arguments to function ‘(env)->GetObjectClass’
52 3)不能把.C文件改成.CPP文件,否则没有规则可以创建“out/apps/JNI_0529/armeabi/objs/JNI_0529/android_jni_MyJNINative.o”需要的目标“apps/JNI_0529/project/jni/android_jni_MyJNINative.c”
53 一个小例子错误真多啊!!NDK不简单啊!!
54 /
55 JNIEXPORT jstring JNICALL Java_com_example_ndkdemo_JniClient_printUser
56 (JNIEnv env, jclass arg, jobject obj)
57 {
58 LOGI(add from jni–打印用户信息–);
59 //获得obj对象的类
60 jclass cls_objClass = (
env)->GetObjectClass(env, obj);
61 /
62 获得obj对象中特定方法getName的id
63 env
64 cls_objClass 方法所属类
65 getName 方法名字
66 ()Ljava/lang/String; 方法签名
67 /
68 jmethodID nameMethodId = (env)->GetMethodID(env, cls_objClass, getName, ()Ljava/lang/String;);
69 /

70 调用obj对象的特定方法getName
71 obj 调用方法的目标对象
72 nameMethodId 调用方法的方法名
73 … 后面还可以添加方法需要的参数
74 /
75 jstring js_name = (jstring)(env)->CallObjectMethod(env, obj, nameMethodId);
76 //将jstring转为c中的字符数组
77 const char
name = (char )(env)->GetStringUTFChars(env, js_name, 0);
78
79 jmethodID ageMethodId = (env)->GetMethodID(env, cls_objClass, getAge, ()I);
80 jint ji_age = (
env)->CallIntMethod(env, obj, ageMethodId);
81
82 jmethodID sexMethodId = (env)->GetMethodID(env, cls_objClass, getSex, ()Ljava/lang/String;);
83 jstring js_sex = (jstring)(
env)->CallObjectMethod(env, obj, sexMethodId);
84 const char sex = (char )(env)->GetStringUTFChars(env, js_sex, 0);
85
86 //打印信息
87 LOGI(user info—-name:%s, age:%d, sex:%s., name, ji_age, sex);
88
89 //释放资源
90 (
env)->ReleaseStringUTFChars(env, js_name, name);
91 (env)->ReleaseStringUTFChars(env, js_sex, sex);
92 // printf(“%s”, str);
93 return js_name;
94 }
95
96 /

97 创建一个对象(调用有参构造函数)
98 /
99 JNIEXPORT jobject JNICALL Java_com_example_ndkdemo_JniClient_newUser
100 (JNIEnv env, jclass arg, jstring name, jint age, jstring sex)
101 {
102 //创建一个class的引用,使用类的全包名
103 jclass cls = (env)->FindClass(env, com/example/ndkdemo/User);
104 //注意这里方法的名称是”“,它表示这是一个构造函数
105 jmethodID id = (
env)->GetMethodID(env, cls, , (Ljava/lang/String;ILjava/lang/String;)V);
106 //获得一实例,后面接构造函数参数
107 // jobject obj = (env)->NewObject(env, cls, id, name, age, sex);
108 jstring jname = (env)->NewStringUTF(env, jni-liuling);
109 jstring jsex = (env)->NewStringUTF(env, jni-男);
110 jobject obj = (
env)->NewObject(env, cls, id, jname, 18L, jsex);
111
112 return obj;
113 }
114
115 /
116 创建一个对象(调用无参构造函数)
117 /
118 JNIEXPORT jobject JNICALL Java_com_example_ndkdemo_JniClient_newUserNoArgs
119 (JNIEnv env, jclass arg, jstring name, jint age, jstring sex)
120 {
121 //创建一个class的引用,使用类的全包名
122 jclass cls = (
env)->FindClass(env, com/example/ndkdemo/User);
123 //注意这里方法的名称是”“,它表示这是一个构造函数
124 jmethodID id = (env)->GetMethodID(env, cls, , ()V);
125 //获得一实例,后面接构造函数参数
126 jobject obj = (
env)->NewObject(env, cls, id);
127 jstring jname = (env)->NewStringUTF(env, jni-liuling);
128 jstring jsex = (
env)->NewStringUTF(env, jni-男);
129
130 //获取jfieldID
131 jfieldID nameId = (env)->GetFieldID(env, cls, name, Ljava/lang/String;);
132 jfieldID ageId = (
env)->GetFieldID(env, cls, age, I);
133 jfieldID sexId = (env)->GetFieldID(env, cls, sex, Ljava/lang/String;);
134 (
env)->SetObjectField(env, obj, nameId, jname);
135 (env)->SetIntField(env, obj, ageId, 18L);
136 (
env)->SetObjectField(env, obj, sexId, jsex);
137
138 return obj;
139 }
140
141 #ifdef __cplusplus
142 }
143 #endif

 

代码注释写的很详细,就不多讲了。

下面创建在jni目录下面创建编译文件Android.mk,内容如下:


1 LOCAL_PATH := $(call my-dir)
2 include $(CLEAR_VARS)
3 LOCAL_MODULE := NDKDemo
4 LOCAL_SRC_FILES := com_example_ndkdemo_JniClient.c
5 LOCAL_LDLIBS:=-L$(SYSROOT)/usr/lib -llog
6 include $(BUILD_SHARED_LIBRARY)

编译过程可以参考网上的,网上很多。下面是在android中调用的代码:


 1 package com.example.ndkdemo;
2
3 import android.os.Bundle;
4 import android.support.v7.app.ActionBarActivity;
5 import android.util.Log;
6 import android.view.View;
7 import android.view.View.OnClickListener;
8 import android.widget.Button;
9 import android.widget.EditText;
10 import android.widget.Toast;
11
12 public class MainActivity extends ActionBarActivity {
13
14 static {
15 System.loadLibrary(“NDKDemo”);
16 }
17
18 @Override
19 protected void onCreate(Bundle savedInstanceState) {
20 super.onCreate(savedInstanceState);
21 setContentView(R.layout.activity_main);
22 findViewById(R.id.button).setOnClickListener(new OnClickListener() {
23 @Override
24 public void onClick(View v) {
25 //调用jni返回字符串
26 //Toast.makeText(MainActivity.this, JniClient.printStr(), Toast.LENGTH_LONG).show();
27 User user = new User(“刘玲”, 25, “男hahaah”);
28 String name = JniClient.printUser(user);
29 Log.e(“name”, name + “”);
30 // User user = JniClient.newUser(“liuling”, 18, “男”);
31 // User user = JniClient.newUserNoArgs(“liuling”, 18, “男”);
32 // Toast.makeText(MainActivity.this, user.toString(), Toast.LENGTH_LONG).show();
33 }
34 });
35 final EditText num1 = (EditText) findViewById(R.id.num1);
36 final EditText num2 = (EditText) findViewById(R.id.num2);
37 final EditText result = (EditText) findViewById(R.id.result);
38 Button addBtn = (Button) findViewById(R.id.addBtn);
39
40 addBtn.setOnClickListener(new OnClickListener() {
41 @Override
42 public void onClick(View v) {
43 int n1 = Integer.valueOf(num1.getText().toString().trim());
44 int n2 = Integer.valueOf(num2.getText().toString().trim());
45 int n3 = JniClient.addInt(n1, n2);
46 result.setText(n3 + “”);
47 }
48 });
49
50 }
51
52
53 }

这里要注意14-16行,一定要加载so文件。