简单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 <stdlib.h> 3 #include <stdio.h> 4 5 // 引入log头文件 6 #include <android/log.h> 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_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 //注意这里方法的名称是"<init>",它表示这是一个构造函数 105 jmethodID id = (*env)->GetMethodID(env, cls, "<init>", "(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 //注意这里方法的名称是"<init>",它表示这是一个构造函数 124 jmethodID id = (*env)->GetMethodID(env, cls, "<init>", "()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文件。
我喜欢,驾驭着代码在风驰电掣中创造完美!我喜欢,操纵着代码在随必所欲中体验生活!我喜欢,书写着代码在时代浪潮中完成经典!每一段新的代码在我手中诞生对我来说就象观看刹那花开的感动!