1.首先创建 DataProvider类:
package com.pl.ndkpassdata; public class DataProvider { static{ System.loadLibrary("passdata");//加载库 } /** * 把两个java中的int传递给c语言, c语言处理完毕后,把相加的结果返回给java */ public native int add(int x,int y); /** * 静态方法 c语言处理减法运算 返回结果给java */ public static native int sub(int x,int y); /** * 把java中的string传递给c语言, c语言获取到java中的string之后 ,在string后面添加 一个hello 字符串 */ public native String getHelloString(String s); /** * 把java中的一个int数组 传递给c语言,c语言处理完毕这个java数组 * 把int数组中的每一个元素+10,然后把结果返回给java */ public native int[] getIntArr(int[] iNum); }
2.创建完毕后找到它.class位置,生成JNI样式的头文件:
com_pl_ndkpassdata_DataProvider.h:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_pl_ndkpassdata_DataProvider */ #ifndef _Included_com_pl_ndkpassdata_DataProvider #define _Included_com_pl_ndkpassdata_DataProvider #ifdef __cplusplus extern "C" { #endif /* * Class: com_pl_ndkpassdata_DataProvider * Method: add * Signature: (II)I */ JNIEXPORT jint JNICALL Java_com_pl_ndkpassdata_DataProvider_add (JNIEnv *, jobject, jint, jint); /* * Class: com_pl_ndkpassdata_DataProvider * Method: sub * Signature: (II)I */ JNIEXPORT jint JNICALL Java_com_pl_ndkpassdata_DataProvider_sub (JNIEnv *, jclass, jint, jint); /* * Class: com_pl_ndkpassdata_DataProvider * Method: getHelloString * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_pl_ndkpassdata_DataProvider_getHelloString (JNIEnv *, jobject, jstring); /* * Class: com_pl_ndkpassdata_DataProvider * Method: getIntArr * Signature: ([I)[I */ JNIEXPORT jintArray JNICALL Java_com_pl_ndkpassdata_DataProvider_getIntArr (JNIEnv *, jobject, jintArray); #ifdef __cplusplus } #endif #endif
上面是用javah -jni命令生成的。
3. 在项目的根目录创建jni目录(和src,bin等目录平级);
1.拷贝com_pl_ndkpassdata_DataProvider.h到jni目录下;
2.在jni目录下创建Android.mk文件和passdata.c文件;(关于c语言的.c和.h文件的区别是 :函数定义要放在.c中,而.h只做声明.)
下面是Android.mk的内容:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := passdata LOCAL_SRC_FILES := passdata.c #liblog.c LOCAL_LDLIBS += -llog include $(BUILD_SHARED_LIBRARY)
下面是passdata.c
#include <stdio.h> #include <jni.h> #include <stdlib.h> #include "com_pl_ndkpassdata_DataProvider.h" #include <android/log.h> #define LOG_TAG "System.out.c" #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中的String转换成c可用的指向字符的指针类型 */ /** * <jni.h>中的指针函数: * jclass (*FindClass)(JNIEnv*, const char*); //拿到一个类 * jstring (*NewStringUTF)(JNIEnv*, const char*);//拿到一个String对象 * jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);//拿到类的方法 * jobject (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...);//调用类的方法 * jsize (*GetArrayLength)(JNIEnv*, jarray);//拿到数组的长度 * jbyte* (*GetByteArrayElements)(JNIEnv*, jbyteArray, jboolean*); //转换成c可用的byte指针类型 */ char* Jstring2CStr(JNIEnv * env,jstring s){ //JNIEnv*:java虚拟机的结构体c语言实现的指针,包含的有很多jni方法 char* rtn = NULL; jclass classstring = (*env)->FindClass(env,"java/lang/String"); //调用java中的String类 jstring strencode = (*env)->NewStringUTF(env,"GB2312"); //拿到编码格式 //拿到方法 。参数2:指定类,参数3:方法名,参数4:(Ljava/lang/String;)是getBytes的参数;[B代表是返回值是一个byte数组 jmethodID mid = (*env)->GetMethodID(env,classstring,"getBytes","(Ljava/lang/String;)[B"); jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env,s,mid,strencode);//相当于调用java的getBytes //拿到byte数组的长度 jsize size = (*env)->GetArrayLength(env,barr); jbyte* ba = (*env)->GetByteArrayElements(env,barr,JNI_FALSE); if(size > 0){ rtn = (char*)malloc(size+1); //申请一块内存 //+" " memcpy(rtn,ba,size); //拷贝字符串 rtn[size]=0; //' ' } (*env)->ReleaseByteArrayElements(env,barr,ba,0);//释放内存空间 return rtn; } JNIEXPORT jint JNICALL Java_com_pl_ndkpassdata_DataProvider_add (JNIEnv * env, jobject obj, jint x, jint y){ LOGD("add()x:%d,y:%d",x,y); return x+y; } JNIEXPORT jint JNICALL Java_com_pl_ndkpassdata_DataProvider_sub (JNIEnv * env, jclass clazz, jint x, jint y){ LOGD("sub()x:%d,y:%d",x,y); return x-y; } JNIEXPORT jstring JNICALL Java_com_pl_ndkpassdata_DataProvider_getHelloString (JNIEnv * env, jobject obj, jstring s){ char* cs = Jstring2CStr(env,s); LOGD("cs=%s",cs); char carr[7] = {' ','h','e','l','l','o',' '}; strcat(cs,carr); //拼接 LOGD("new cs=%s",cs); LOGD("end getHelloString()"); return (*env)->NewStringUTF(env,cs); } //jsize (*GetArrayLength)(JNIEnv*, jarray); //拿到数组长度 // jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*) //转换成c可用的int指针类型 JNIEXPORT jintArray JNICALL Java_com_pl_ndkpassdata_DataProvider_getIntArr (JNIEnv * env, jobject obj, jintArray jarr){ jsize len = (*env)->GetArrayLength(env,jarr); LOGD("len=%d",len); jint* carr = (*env)->GetIntArrayElements(env,jarr,JNI_FALSE); int i; for(i=0;i<len;i++){ *(carr+i)+=10; } LOGD("end getIntArr()"); return jarr; }
完成映射实现,使用Cygwin生成.so库文件.基本完成了,别忘了clean下项目。
基本到这做完了。下面是测试代码:
MainActivity.java
package com.pl.ndkpassdata; import android.os.Bundle; import android.app.Activity; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.Toast; public class MainActivity extends Activity implements OnClickListener{ private Button bt1,bt2,bt3,bt4; private DataProvider provider; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); bt1 = (Button) this.findViewById(R.id.bt1); bt2 = (Button) this.findViewById(R.id.bt2); bt3 = (Button) this.findViewById(R.id.bt3); bt4 = (Button) this.findViewById(R.id.bt4); bt1.setOnClickListener(this); bt2.setOnClickListener(this); bt3.setOnClickListener(this); bt4.setOnClickListener(this); provider = new DataProvider(); } public void onClick(View v) { switch (v.getId()) { case R.id.bt1: int result = provider.add(10, 10); Toast.makeText(this, "相加的结果:"+result, 1).show(); break; case R.id.bt2: String str = provider.getHelloString("pengliang"); Toast.makeText(this, str, 1).show(); break; case R.id.bt3: int[] arr = {7,8,9,10,6}; provider.getIntArr(arr); String arrtostr ="["; for(int i=0;i<arr.length;i++){ arrtostr+=arr[i]+","; } arrtostr +="]"; Toast.makeText(this, arrtostr, 1).show(); break; case R.id.bt4: int subresult = DataProvider.sub(100, 24); Toast.makeText(this, "相减的结果:"+subresult, 1).show(); break; } } }
main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <Button android:id="@+id/bt1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="传递两个int给c代码" /> <Button android:id="@+id/bt2" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="传递string给c代码" /> <Button android:id="@+id/bt3" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="传递int数组给c代码" /> <Button android:id="@+id/bt4" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="调用静态native方法" /> </LinearLayout>