android JNI笔记

本文详细介绍了如何利用C++与Java的JNI技术实现跨平台通讯,包括创建动态库、调用Java方法、Java调用C++代码等关键步骤。通过实践例子展示了从创建Android.mk文件、编译生成动态库到Java代码调用C++方法的全过程,旨在帮助开发者理解并掌握跨语言通讯的基本原理与实现方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自:http://blog.youkuaiyun.com/xjwangliang/article/details/7066368


第一个例子


    //Hello.c文件:  
    #include <string.h>  
    #include <jni.h>  
    jstring Java_org_wangliang_ndktest_MainActivity_getGreetings(JNIEnv *env, jobject javaThis) {  
      return (*env)->NewStringUTF(env, "Hello from native code!");  
    }  


JNIEnv 类型代表了java环境 通过JNIEnv* 指针,就可以对java端的代码进行操作.

1、创建java类的对象,调用java对象的方法

2、获取java对象的属性 等等.

jobject obj 就是当前方法所在的类

    //Android.mk文件  
    LOCAL_PATH := $(call my-dir)#此文件目录  
      
    include $(CLEAR_VARS)  
    #库的名字(会加上lib)  
    LOCAL_MODULE    := Greet  
    #源文件      
    LOCAL_SRC_FILES := Hello.c  
    #动态库  
    include $(BUILD_SHARED_LIBRARY)  


Android.mk 的含义

LOCAL_PATH:=$(call my-dir)

LOCAL_PATH是定义源文件目录.my-dir 是个定义的宏方法, $(call my-dir)就是调用这个叫 my-dir的宏方法,这个方法返回值就是Android.mk文件所在的目录

include $(CLEAR_VARS)

CLEAR_BARS 变量是build system里面的一个变量,这个变量指向了所有的类似 LOCAL_XXX的变量,

执行完这一句话这个编译系统就把所有的类似LOCAL_MODULE,LOCAL_SRC_FILES,LOCAL_STATIC_LIBRARIES,...这样的变量都清除掉,但是不会清除掉 LOCAL_PATH

LOCAL_MODULE  就是你要生成的库的名字,这个名字要是唯一的.不能有空格.编译后系统会自动在前面加上lib的头比如说我们的Hello 就编译成了libHello.so还有个特点就是如果你起名叫libHello 编译后ndk就不会给你的module名字前加上lib了,但是你最后调用的时候 还是调用Hello这个库

LOCAL_SRC_FILES = :Hello.c这个是指定你要编译哪些文件,不需要指定头文件 ,引用哪些依赖因为编译器会自动找到这些依赖自动编译

include $(BUILD_SHARED_LIBRARY)  BUILD_STATIC_LIBRARY.so编译后生成的库的类型,如果是静态库,配置include $(BUILD_STATIC_LIBRARY)

别的参数:

LOCAL_CPP_EXTENSION := cc //指定c++文件的扩展名

LOCAL_MODULE    := ndkfoo 

LOCAL_SRC_FILES := ndkfoo.cc

LOCAL_LDLIBS += -llog -lvmsagent -lmpnet -lmpxml -lH264Android 指定需要加载一些别的什么库.

    <strong>  
    </strong>  

    <strong>主文件org.wangliang.ndktest.MainActivity:</strong>  

    //主文件org.wangliang.ndktest.MainActivity:  
    package org.wangliang.ndktest;  
      
    import android.app.Activity;  
    import android.os.Bundle;  
    import android.view.View;  
    import android.widget.Toast;  
      
    public class MainActivity extends Activity {  
        static{  
            System.loadLibrary("Greet");//载入库(库的名字libGreet.so)  
        }  
        public native String getGreetings();  
        public void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
            setContentView(R.layout.main);  
        }  
        public void get(View v){  
            String greetings = getGreetings();//调用C的方法  
            Toast.makeText(this, greetings, 0).show();  
        }  
    }  


使用javah输出头文件


    Javah:  
    C:\>javah -classpath E:\wangliang\JAVAEE_Work\NdkProject\bin  -d E:\   org.wangliang.ndktest.MainActivity  


javap获得方法签名

    javap -s 路径  
    C:\Documents and Settings\Administrator>javap -s -classpath E:\Soft\workspace\Inten  
    tReceiver\bin org.wangliang.intentreceiver.Signature  
    Compiled from "Signature.java"  
    public class org.wangliang.intentreceiver.Signature extends java.lang.Object{  
    public org.wangliang.intentreceiver.Signature();  
      Signature: ()V  
    public static void main(java.lang.String[]);  
      Signature: ([Ljava/lang/String;)V  
    public void method1(byte, char, short, int, long);  
      Signature: (BCSIJ)V  
    public void method2(java.lang.String, boolean, float, double);  
      Signature: (Ljava/lang/String;ZFD)V  
    public void method3(java.sql.Date);  
      Signature: (Ljava/sql/Date;)V  
    public void method4(byte[], char[], short[], int[], long[]);  
      Signature: ([B[C[S[I[J)V  
    public java.util.List method5(java.lang.String[], boolean[], float[], double[]);  
      Signature: ([Ljava/lang/String;[Z[F[D)Ljava/util/List;  
    }  
    对象和对象数组后加上;  


JNI中使用logcat


l 1Android.mk文件增加:LOCAL_LDLIBS += -llog

l 2C代码中增加


    #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__)  
    使用  
    LOGI("log from c code ");//一个参数(因为就相当于printf)  
    LOGI("x= %ld",x);      //两个参数(因为就相当于printf)  
    LOGD("y= %ld",y);  



C中调用java代码(在一个方法A中调用java,此方法A最终得被java调用)

1、通过类名获得

2类、方法名、签名获得方法id

3、通过方法id和调用对象obj执行方法

具体的

1、 jclass class = (*env)->FindClass(env,”完整类名”)

2、 jmethodId id = (*env)GetMethodId(env,class,”方法名”,”方法签名”)

3、(*env)->Callvoke<Type>Method(env,obj ,id);

或者(*env)->CallStatic<Type>Method(env,id,obj);


Java中调用C代码

1、 在java中声明native方法(如public static native void method()

2、 可以自己写C代码:导入包jni.hstring.h(若要记录信息,还要android/log.h,当然就要定义宏了),编写java中与native方法相应的的方法。其中方法名为Java_包名_类名_方法名,方法参数为(JNIEnv *env, jobject javaThis,native方法中的参数列表)

3、 不用2的方法,使用javah输出头文件,然后自己编写一个c文件,include刚才的头文件,把里面的方法拷贝过来,然后实现方法。但是机器生成的头文件的方法声明不太一样,会在方法返回值其那后分别加上JNIEXPORT 和JNICALL,比如:JNIEXPORT void JNICALL。



小结:

1.创建一个android工程

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

3.javah工具生成头文件

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

5.编写Android.mk文件

6.Ndk编译生成动态库

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



例子:



    //DataProvider.java  

    //==========  
    package org.wangliang.ndk01.test;  
      
    import android.content.Context;  
    import android.widget.Toast;  
      
    public class DataProvider {  
            Context context;  
            public DataProvider(Context context){  
                this.context = context;  
            }  
            public native void greetToC(String greetings);  
              
            public void greetToJava(String greetings){  
                String text = " "+greetings+" to JAVA";  
                Toast.makeText(context, text, 1).show();  
            }  
    }  


    //Activity01.java  

    //=================  
    package org.wangliang.ndk01.test;  
    import android.app.Activity;  
    import android.os.Bundle;  
    import android.view.View;  
    import android.widget.EditText;  
    public class Activity1 extends Activity {  
    private EditText text;  
    private DataProvider provider;  
    static{  
    System.loadLibrary("Greet01");  
    }  
    /** 
     * java-->c-->java 
     */  
        public void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
            setContentView(R.layout.main);  
            text = (EditText) findViewById(R.id.text);  
            provider = new DataProvider(this);  
        }  
        public void greet(View v){  
            String greetings = text.getText().toString();  
            provider.greetToC(greetings);  
        }  
    }  


    Main.xml  

    <?xml version="1.0" encoding="utf-8"?>  
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
        android:orientation="vertical" android:layout_width="fill_parent"  
        android:layout_height="fill_parent">  
    <EditText   
        android:id="@+id/text"   
        android:layout_width="fill_parent"   
        android:layout_height="wrap_content"/>  
    <Button   
        android:text="问候"  
        android:layout_width="fill_parent"   
        android:layout_height="wrap_content"  
        android:gravity="center"  
        android:onClick="greet"/>  
    </LinearLayout>  


编写C文件
   位置:/NDK01/jni/org_wangliang_ndk01_test_DataProvider.h  
      
    /* DO NOT EDIT THIS FILE - it is machine generated */  
    #include <jni.h>  
    /* Header for class org_wangliang_ndk01_test_DataProvider */  
      
    #ifndef _Included_org_wangliang_ndk01_test_DataProvider  
    #define _Included_org_wangliang_ndk01_test_DataProvider  
    #ifdef __cplusplus  
    extern "C" {  
    #endif  
    /* 
     * Class:     org_wangliang_ndk01_test_DataProvider 
     * Method:    greetToC 
     * Signature: (Ljava/lang/String;)V 
     */  
    JNIEXPORT void JNICALL Java_org_wangliang_ndk01_test_DataProvider_greetToC  
      (JNIEnv *, jobject, jstring);  
      
    #ifdef __cplusplus  
    }  
    #endif  
    #endif  
      
    位置:/NDK01/jni/Greet01.c  
    #include "org_wangliang_ndk01_test_DataProvider.h"  
    #include <string.h>  
    //jstring--> char*   
    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");  
         // 相当于String .getByte("GB2312");  
         jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env,jstr,mid,strencode);   
         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 void JNICALL Java_org_wangliang_ndk01_test_DataProvider_greetToC  
      (JNIEnv *env, jobject obj, jstring greetings){  
        char* className = "org/wangliang/ndk01/test/DataProvider";  
        jclass clazz = (*env)->FindClass(env,className);  
        jmethodID methodID = (*env)->GetMethodID(env,clazz,"greetToJava","(Ljava/lang/String;)V");  
        char* text = strcat(Jstring2CStr(env,greetings),"to C and Trans ");  
        (*env)->CallVoidMethod(env,obj,methodID,(*env)->NewStringUTF(env,text));  
      
    }  



编写Android.mk文件
    LOCAL_PATH := $(call my-dir)#此文件目录  
    include $(CLEAR_VARS)  
    #库的名字(会加上lib)  
    LOCAL_MODULE    := Greet01  
    #源文件  
    LOCAL_SRC_FILES := Greet01.c  
    #动态库  
    include $(BUILD_SHARED_LIBRARY)  


最后$ ndk-build

结果

在再写几个简单的函数

         做更多的事情:  

    #include "org_wangliang_ndk01_test_DataProvider.h"  
    #include <string.h>  
    #include <stdlib.h>  
      
    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");  
         // 相当于String .getByte("GB2312");  
         jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env,jstr,mid,strencode);   
         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;  
    }  
    /* 
     * Class:     org_wangliang_ndk01_test_DataProvider 
     * Method:    greetToC 
     * Signature: (Ljava/lang/String;)V 
     */  
    JNIEXPORT void JNICALL Java_org_wangliang_ndk01_test_DataProvider_greetToC //调用java的greetToJava  
      (JNIEnv *env, jobject obj, jstring greetings){  
        char* className = "org/wangliang/ndk01/test/DataProvider";  
        jclass clazz = (*env)->FindClass(env,className);  
        jmethodID methodID = (*env)->GetMethodID(env,clazz,"greetToJava","(Ljava/lang/String;)V");  
        char* text = strcat(Jstring2CStr(env,greetings),"to C and Trans ");  
        (*env)->CallVoidMethod(env,obj,methodID,(*env)->NewStringUTF(env,text));  
      
    }  
    /* 
     * Class:     org_wangliang_ndk01_test_DataProvider 
     * Method:    swap01 
     * Signature: ([III)V 
     */  
    JNIEXPORT void JNICALL Java_org_wangliang_ndk01_test_DataProvider_swap01//不返回值交换数组数据  
      (JNIEnv *env, jobject obj, jintArray arr, jint i, jint j){  
              int len = (*env)->GetArrayLength(env,arr);//c语言中没法在被调用的方法中获得数组长度(因为传入的是指针,sizeof(p)==4)  
              if(len = 0)return ;  
              jint* p = (*env)->GetIntArrayElements(env,arr,0);  
              jint tmp = *(p+i);  
              *(p+i) = *(p+j);  
              *(p+j) = tmp;  
        
      }  
      
    /* 
     * Class:     org_wangliang_ndk01_test_DataProvider 
     * Method:    swap02 
     * Signature: ([III)[I 
     */  
    JNIEXPORT jintArray JNICALL Java_org_wangliang_ndk01_test_DataProvider_swap02//返回值交换数组数据  
      (JNIEnv *env, jobject obj, jintArray arr, jint i, jint j){  
              int len = (*env)->GetArrayLength(env,arr);  
              if(len = 0)return ;  
              jint* p = (*env)->GetIntArrayElements(env,arr,0);  
              jint tmp = *(p+i);  
              *(p+i) = *(p+j);  
              *(p+j) = tmp;        
              return arr;    
      }  
      
    /* 
     * Class:     org_wangliang_ndk01_test_DataProvider 
     * Method:    addANum 
     * Signature: ([II)[I 
     */  
    JNIEXPORT jintArray JNICALL Java_org_wangliang_ndk01_test_DataProvider_addANum//数组元素加上一个数  
      (JNIEnv *env, jobject obj, jintArray arr, jint num){  
        // 1.获取到 arr的大小  
      
        int len = (*env)->GetArrayLength(env, arr);  
        //上面为什么是jsize alen = (*env)->GetArrayLength(env,barr);而且memcpy(rtn,ba,alen);  
        if(len==0){  
            return arr;  
        }  
        jint* p = (*env)-> GetIntArrayElements(env,arr,0);  
        int i=0;  
        for(;i<len;i++){  
            *(p+i) += num;  
        }  
      
        return arr;  
      }  
    /* 
     * Class:     org_wangliang_ndk01_test_DataProvider 
     * Method:    getCondition 
     * Signature: (I)Ljava/lang/String; 
     */  
    JNIEXPORT jstring JNICALL Java_org_wangliang_ndk01_test_DataProvider_getCondition  
      (JNIEnv *env, jobject obj, jint day){//jint如何转成int->char*(另一个问题char*-->int?)  
          //需要调用static int  getSales(int day)  
          //  
          char* className = "org/wangliang/ndk01/test/DataProvider";  
          jclass clazz = (*env)->FindClass(env,className);  
          jmethodID methodID = (*env)->GetStaticMethodID(env,clazz,"getSales","(I)I");//有静态static  
          //jint sales = (*env)->CallStaticIntMethod(env,clazz,methodID,1);//正确  
          long sales = (*env)->CallStaticIntMethod(env,clazz,methodID,day);//正确(jint和int都行?)  
          char* condition ;//"The sales in ...";  
          char* d;//2  
          //itoa((int)sales, condition, 10);//stdlib.h中没有itoa方法(怎么把数字转变成字符串)  
          //itoa((int)day, d, 10);  
          //condition = strcat(strcat("The sales in ",d),condition);  
          return (*env)->NewStringUTF(env,"hello world");  
        
      }  




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值