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 a14 * @param b15 * @return16 */17 static public native int addInt(int a, int b);18 19 /**20 * 通过JNI输入JAVA对象信息21 * @param user22 * @return23 */24 static public native String printUser(User user);25 26 /**27 * 通过JNI创建java对象28 * @param name29 * @param age30 * @param sex31 * @return32 */33 static public native User newUser(String name, int age, String sex);34 35 /**36 * 通过JNI调用无参构造函数创建java对象并且设置相应属性值37 * @param name38 * @param age39 * @param sex40 * @return41 */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 #include3 #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/proje*ct/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_newUser100 (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_newUserNoArgs119 (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 //获取jfieldID131 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 __cplusplus142 }143 #endif
代码注释写的很详细,就不多讲了。
下面创建在jni目录下面创建编译文件Android.mk,内容如下:
1 LOCAL_PATH := $(call my-dir)2 include $(CLEAR_VARS)3 LOCAL_MODULE := NDKDemo4 LOCAL_SRC_FILES := com_example_ndkdemo_JniClient.c5 LOCAL_LDLIBS:=-L$(SYSROOT)/usr/lib -llog6 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 @Override19 protected void onCreate(Bundle savedInstanceState) {20 super.onCreate(savedInstanceState);21 setContentView(R.layout.activity_main);22 findViewById(R.id.button).setOnClickListener(new OnClickListener() {23 @Override24 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 @Override42 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文件。