初学JNI(二)调用C\C++中的方法
布局文件:
<?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" android:gravity="center"> <TextView android:id="@+id/tv_call" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Hello World, MyActivity" android:padding="10dp" android:gravity="center" /> <Button android:id="@+id/btn_call" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Call" /> <Button android:id="@+id/btn_send_int" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="传递int参数" /> <Button android:id="@+id/btn_send_str" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="传递String参数" /> <Button android:id="@+id/btn_ints" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="传递int数组" /> </LinearLayout>
Java代码:
public class MyActivity extends Activity implements View.OnClickListener { DataProvider mPro; /** * 使用静态代码块加载库文件 */ static { System.loadLibrary("Hello"); } public native String helloFromJNI (); /** * Called when the activity is first created. */ @Override public void onCreate (Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mPro = new DataProvider(); initView(); } TextView mTvCall; private void initView () { mTvCall = (TextView) findViewById(R.id.tv_call); Button btnCall = (Button) findViewById(R.id.btn_call); Button btnInts = (Button) findViewById(R.id.btn_ints); Button btnInt = (Button) findViewById(R.id.btn_send_int); Button btnStr = (Button) findViewById(R.id.btn_send_str); btnCall.setOnClickListener(this); btnInt.setOnClickListener(this); btnInts.setOnClickListener(this); btnStr.setOnClickListener(this); } @Override public void onClick (View v) { int id = v.getId(); switch (id) { case R.id.btn_call: //调用C\C++中的方法输出"你好 from C++." mTvCall.setText(helloFromJNI()); Toast.makeText(MyActivity.this, helloFromJNI(), Toast.LENGTH_LONG).show(); break; case R.id.btn_send_int: //向C++代码中传递两个int型参数 mTvCall.setText(String.valueOf(mPro.add(10, 20))); break; case R.id.btn_send_str: //向C++代码中传递一个String型参数 mTvCall.setText(mPro.sayHello(" JNI")); break; case R.id.btn_ints: //向C++代码中传递int数组 int[] arr = mPro.intMethod(new int[]{30, 40}); StringBuilder sb = new StringBuilder(); for (int i = 0; i < arr.length; i++) { if (i == arr.length - 1) sb.append(arr[i]); else sb.append(arr[i]).append(","); } mTvCall.setText(sb.toString()); break; } } }
在定义native方法时,并不推荐直接定义在Activity中,而是专门建立一个定义native方法的类:
*/ public class DataProvider { static{ System.loadLibrary("Hello"); } public native int add(int x,int y); public native String sayHello(String str); public native int[] intMethod(int[] numbers); }
C++代码的实现:
#include <iostream> #include <string> #include <malloc.h> #include <jni.h> #include "com_chen_jni_demo_MyActivity.h" #include "com_chen_jni_demo_DataProvider.h" using namespace std; //导入Log #include <android/log.h> #define LOG_TAG "System.out" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) //jni.h没有导入的话,编译时会出现符号错误的问题 //使用C++实现输出 JNIEXPORT jstring JNICALL Java_com_chen_jni_demo_MyActivity_helloFromJNI (JNIEnv * env, jobject obj){ char* cstr = "你好 from C++."; LOGI("DEBUG %s",cstr); return (*env).NewStringUTF(cstr); } //向C++代码中传递一个String型参数 JNIEXPORT jstring JNICALL Java_com_chen_jni_demo_DataProvider_sayHello (JNIEnv * env, jobject obj, jstring jstr){ //声明要调用的函数 char* jstringTostring(JNIEnv* env, jstring jstr); //将java字符串转换为c++中的字符数组 char* str1=jstringTostring(env,jstr); char* str2="你好"; //拼接字符数组 strcat(str1,str2); return (*env).NewStringUTF(str1); } //向C++代码中传递两个int型参数 JNIEXPORT jint JNICALL Java_com_chen_jni_demo_DataProvider_add (JNIEnv * env, jobject obj, jint x, jint y){ return x+y; } //向C++代码中传递一个int数组 JNIEXPORT jintArray JNICALL Java_com_chen_jni_demo_DataProvider_intMethod (JNIEnv * env,jobject obj, jintArray jintArr){ //获得整型数组的长度 jsize size = (*env).GetArrayLength(jintArr); //jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*); //jboolean* 表示获得数据的方式 true-1-复制 false-0-引用 c语言中 jint* arr = (*env).GetIntArrayElements(jintArr,false); for(int i=0;i<size;i++){ //第一种 //*(arr+i) = *(arr+i)+10; //*(arr+i) +=10; /* 第二种 void (*SetIntArrayRegion)(JNIEnv*, jintArray,jsize, jsize, const jint*); 设置IntArray中指定范围内元素的值 参数3:范围的起始位置 参数4:范围的擦海南过度(指定修改的元素的个数) 参数5:const jint* 指定元素的值(地址) */ int temp = *(arr+i)+10; (*env).SetIntArrayRegion(jintArr,i,1,&temp); } //使用的是引用方式,直接返回即可,数据已经改变 return jintArr; } /** *将Java的String转换为char* */ jstring stoJstring (JNIEnv*env, const char*pat) { jclass strClass = env -> FindClass("Ljava/lang/String;"); jmethodID ctorID = env -> GetMethodID(strClass, "", "([BLjava/lang/String;)V"); jbyteArray bytes = env -> NewByteArray(strlen(pat)); env -> SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte *)pat); jstring encoding = env -> NewStringUTF("utf-8"); return (jstring) env -> NewObject(strClass, ctorID, bytes, encoding); } //invoke function JNIEXPORT jstring JNICALL Java_test_cs_web_SWIFTAlianceCASmfTest_strcal (JNIEnv*env, jclass obj, jstring jstr1, jstring jstr2) { jbyteArray bytes = 0; jthrowable exc; char*pszResult = NULL; char*pszSTR1 = NULL; char*pszSTR2 = NULL; pszSTR1 = jstringTostring(env, jstr1); pszSTR2 = jstringTostring(env, jstr2); int nlen = sizeof(char)*(strlen(pszSTR1) + strlen(pszSTR2)); pszResult = (char*)malloc(nlen); strcpy(pszResult, pszSTR1); strcat(pszResult, pszSTR2); jstring jstrRe = stoJstring(env, pszResult); free(pszSTR1); free(pszSTR2); free(pszResult); return (jstrRe); }