Android中使用JNI七个步骤:

1.创建一个android工程

2.JAVA代码中写声明native 方法 public native String helloFromJNI();

3.用javah工具生成头文件

4. 创建jni目录,引入头文件,根据头文件实现c代码

5.编写Android.mk文件

6.Ndk编译生成动态库

7.Java代码load 动态库.调用native代码

Java调用C进行数据传递

这里分别传递整形、字符串、数组在C中进行处理。

声明native 方法:

  • public class DataProvider {
  • // 两个java中的int 传递c 语言 ,  c语言处理这个相加的逻辑,把相加的结果返回给java
  • public native int add( int x , int y);
  • //把一个java中的字符串传递给c语言, c 语言处理下字符串, 处理完毕返回给java
  • public native String sayHelloInC(String s);
  • //把一个java中int类型的数组传递给c语言, c语言里面把数组的每一个元素的值 都增加5,
  • //然后在把处理完毕的数组,返回给java
  • public native int[] intMethod( int[] iNum);
  • 下面是生成的头文件,参数: JENEnv* 代表的是java环境 , 通过这个环境可以调用java的方法,jobject 表示哪个对象调用了 这个c语言的方法, thiz就表示的是当前的对象

    [cpp] view plain copy
  • /* DO NOT EDIT THIS FILE - it is machine generated */
  • #include <jni.h>
  • /* Header for class cn_itcast_ndk3_DataProvider */
  • #ifndef _Included_cn_itcast_ndk3_DataProvider
  • #define _Included_cn_itcast_ndk3_DataProvider
  • #ifdef __cplusplus
  • extern "C" {
  • #endif
  • * Class:     cn_itcast_ndk3_DataProvider
  • * Method:    add
  • * Signature: (II)I
  • JNIEXPORT jint JNICALL Java_cn_itcast_ndk3_DataProvider_add
  • (JNIEnv *, jobject, jint, jint);
  • * Class:     cn_itcast_ndk3_DataProvider
  • * Method:    sayHelloInC
  • * Signature: (Ljava/lang/String;)Ljava/lang/String;
  • JNIEXPORT jstring JNICALL Java_cn_itcast_ndk3_DataProvider_sayHelloInC
  • (JNIEnv *, jobject, jstring);
  • * Class:     cn_itcast_ndk3_DataProvider
  • * Method:    intMethod
  • * Signature: ([I)[I
  • JNIEXPORT jintArray JNICALL Java_cn_itcast_ndk3_DataProvider_intMethod
  • (JNIEnv *, jobject, jintArray);
  • #ifdef __cplusplus
  • #endif
  • #endif
  • C代码出了要引用头文件外,还要引入日志信息,以方便在C 中进行调试

    [cpp] view plain copy
  • #define LOG_TAG "logfromc"
  • //日志显示的等级
  • #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
  • #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
  • // java中的jstring, 转化为c的一个字符数组
  • char*   Jstring2CStr(JNIEnv*   env,   jstring   jstr)
  • char*   rtn   =   NULL;
  • jclass   clsstring   =   (*env)->FindClass(env, "java/lang/String");
  • jstring   strencode   =   (*env)->NewStringUTF(env, "GB2312");
  • jmethodID   mid   =   (*env)->GetMethodID(env,clsstring, "getBytes", "(Ljava/lang/String;)[B");
  • jbyteArray   barr=   (jbyteArray)(*env)->CallObjectMethod(env,jstr,mid,strencode); // String .getByte("GB2312");
  • jsize   alen   =   (*env)->GetArrayLength(env,barr);
  • jbyte*   ba   =   (*env)->GetByteArrayElements(env,barr,JNI_FALSE);
  • if(alen   >   0)
  • rtn   =   ( char*)malloc(alen+1); //new   char[alen+1]; "\0"
  • memcpy(rtn,ba,alen);
  • rtn[alen]=0;
  • (*env)->ReleaseByteArrayElements(env,barr,ba,0); //释放内存
  • return rtn;
  • //处理整形相加
  • JNIEXPORT jint JNICALL Java_cn_itcast_ndk3_DataProvider_add
  • (JNIEnv * env, jobject obj, jint x, jint y){
  • //打印 java 传递过来的 jstring ;
  • LOGI( "log from c code ");
  • LOGI( "x= %ld",x);
  • LOGD( "y= %ld",y);
  • return x+y;
  • //处理字符串追加
  • JNIEXPORT jstring JNICALL Java_cn_itcast_ndk3_DataProvider_sayHelloInC
  • (JNIEnv * env, jobject obj, jstring str){
  • char* p =  Jstring2CStr(env,str);
  • LOGI( "%s",p);
  • char* newstr = "append string";
  • //strcat(dest, sorce) 把sorce字符串添加到dest字符串的后面
  • LOGI( "END");
  • return (*env)->NewStringUTF(env, strcat(p,newstr));
  • //处理数组中的每一个元素
  • JNIEXPORT jintArray JNICALL Java_cn_itcast_ndk3_DataProvider_intMethod
  • (JNIEnv * env, jobject obj, jintArray arr){
  • // 1.获取到 arr的大小
  • int len = (*env)->GetArrayLength(env, arr);
  • LOGI( "len=%d", len);
  • if(len==0){
  • return arr;
  • //取出数组中第一个元素的内存地址
  • jint* p = (*env)-> GetIntArrayElements(env,arr,0);
  • int i=0;
  • for(;i<len;i++){
  • LOGI( "len=%ld", *(p+i)); //取出的每个元素
  • *(p+i) += 5; //取出的每个元素加五
  • return arr;
  • 编写Android.mk文件

    [cpp] view plain copy
  • LOCAL_MODULE    := Hello
  • LOCAL_SRC_FILES := Hello.c
  • #增加 log 函数对应的log 库  liblog.so  libthread_db.a
  • LOCAL_LDLIBS += -llog
  • include $(BUILD_SHARED_LIBRARY)
  • Java代码load 动态库.调用native代码

    [java] view plain copy
  • @Override
  • public void onCreate(Bundle savedInstanceState) {
  • super.onCreate(savedInstanceState);
  • setContentView(R.layout.main);
  • dp = new DataProvider();
  • //add对应的事件
  • public void add(View view){
  • //执行C语言处理数据
  • int result = dp.add( 3, 5);
  • Toast.makeText( this, "相加的结果"+ result, 1).show();
  • C中回调java方法

    声明native 方法:

    [java] view plain copy
  • public class DataProvider{
  • public native void callCcode();
  • public native void callCcode1();
  • public native void callCcode2();
  • ///C调用java中的空方法
  • public void helloFromJava(){
  • System.out.println( "hello from java ");
  • //C调用java中的带两个int参数的方法
  • public int Add( int x, int y){
  • System.out.println( "相加的结果为"+ (x+y));
  • return x+y;
  • //C调用java中参数为string的方法
  • public void printString(String s){
  • System.out.println( "in java code "+ s);
  • 头文件可以用jdk自带的javah进行自动生成,使用javap -s可以获取到方法的签名。

    C代码实现回调需要三个步骤:首先要要获取到 某个对象 , 然后获取对象里面的方法  ,最后 调用这个方法  .

    [cpp] view plain copy
  • #include <android/log.h>
  • #define LOG_TAG "logfromc"
  • #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
  • #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
  • //1.调用java中的无参helloFromJava方法
  • JNIEXPORT void JNICALL Java_cn_itcast_ndk4_DataProvider_callCcode
  • (JNIEnv * env , jobject obj){
  • // 获取到DataProvider对象
  • char* classname = "cn/itcast/ndk4/DataProvider";
  • jclass dpclazz = (*env)->FindClass(env,classname);
  • if (dpclazz == 0) {
  • LOGI( "not find class!");
  • } else
  • LOGI( "find class");
  • //第三个参数 和第四个参数 是方法的签名,第三个参数是方法名  , 第四个参数是根据返回值和参数生成的
  • //获取到DataProvider要调用的方法
  • jmethodID methodID = (*env)->GetMethodID(env,dpclazz, "helloFromJava", "()V");
  • if (methodID == 0) {
  • LOGI( "not find method!");
  • } else
  • LOGI( "find method");
  • //调用这个方法
  • (*env)->CallVoidMethod(env, obj,methodID);
  • // 2.调用java中的printString方法传递一个字符串
  • JNIEXPORT void JNICALL Java_cn_itcast_ndk4_DataProvider_callCcode1
  • (JNIEnv * env, jobject obj){
  • LOGI( "in code");
  • // 获取到DataProvider对象
  • char* classname = "cn/itcast/ndk4/DataProvider";
  • jclass dpclazz = (*env)->FindClass(env,classname);
  • if (dpclazz == 0) {
  • LOGI( "not find class!");
  • } else
  • LOGI( "find class");
  • // 获取到要调用的method
  • jmethodID methodID = (*env)->GetMethodID(env,dpclazz, "printString", "(Ljava/lang/String;)V");
  • if (methodID == 0) {
  • LOGI( "not find method!");
  • } else
  • LOGI( "find method");
  • //调用这个方法
  • (*env)->CallVoidMethod(env, obj,methodID,(*env)->NewStringUTF(env, "haha"));
  • // 3. 调用java中的add方法 , 传递两个参数 jint x,y
  • JNIEXPORT void JNICALL Java_cn_itcast_ndk4_DataProvider_callCcode2
  • (JNIEnv * env, jobject obj){
  • char* classname = "cn/itcast/ndk4/DataProvider";
  • jclass dpclazz = (*env)->FindClass(env,classname);
  • jmethodID methodID = (*env)->GetMethodID(env,dpclazz, "Add", "(II)I");
  • (*env)->CallIntMethod(env, obj,methodID,3l,4l);
  •