本文主要是介绍NDK开发基本知识,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
(3) NDK开发中乱码问题
- #include <jni.h>
- /*
- * 方法名称规定 : Java_完整包名类名_方法名()
- * JNIEnv 指针
- *
- * 参数介绍 :
- * env : 代表Java环境, 通过这个环境可以调用Java中的方法
- * thiz : 代表调用JNI方法的对象, 即MainActivity对象
- */
- jstring Java_shuliang_han_ndkhelloworld_MainActivity_helloFromJNI(JNIEnv *env, jobject thiz)
- {
- /*
- * 调用 android-ndk-r9c\platforms\android-8\arch-arm\usr\include 中jni.h中的方法
- * jni.h 中定义的方法 jstring (*NewStringUTF)(JNIEnv*, const char*);
- */
- return (*env)->NewStringUTF(env, "hello world jni 中文");
- }
使用NDK重新编译hello.c文件 : 修改了C源码之后, 重新将该c文件编译成so文件;
- 01-31 14:36:04.803: W/dalvikvm(389): JNI WARNING: illegal continuation byte 0xd0
- 01-31 14:36:04.803: W/dalvikvm(389): string: 'hello world jni ????'
- 01-31 14:36:04.803: W/dalvikvm(389): in Lshuliang/han/ndkhelloworld/MainActivity;.helloFromJNI ()Ljava/lang/String; (NewStringUTF)
- 01-31 14:36:04.834: I/dalvikvm(389): "main" prio=5 tid=1 NATIVE
- 01-31 14:36:04.834: I/dalvikvm(389): | group="main" sCount=0 dsCount=0 obj=0x4001f1a8 self=0xce48
- 01-31 14:36:04.834: I/dalvikvm(389): | sysTid=389 nice=0 sched=0/0 cgrp=default handle=-1345006528
- 01-31 14:36:04.844: I/dalvikvm(389): | schedstat=( 257006717 305462830 51 )
- 01-31 14:36:04.844: I/dalvikvm(389): at shuliang.han.ndkhelloworld.MainActivity.helloFromJNI(Native Method)
- 01-31 14:36:04.844: I/dalvikvm(389): at shuliang.han.ndkhelloworld.MainActivity.onCreate(MainActivity.java:26)
- 01-31 14:36:04.844: I/dalvikvm(389): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
- 01-31 14:36:04.853: I/dalvikvm(389): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1611)
- 01-31 14:36:04.853: I/dalvikvm(389): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663)
- 01-31 14:36:04.853: I/dalvikvm(389): at android.app.ActivityThread.access$1500(ActivityThread.java:117)
- 01-31 14:36:04.864: I/dalvikvm(389): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:931)
- 01-31 14:36:04.864: I/dalvikvm(389): at android.os.Handler.dispatchMessage(Handler.java:99)
- 01-31 14:36:04.864: I/dalvikvm(389): at android.os.Looper.loop(Looper.java:123)
- 01-31 14:36:04.864: I/dalvikvm(389): at android.app.ActivityThread.main(ActivityThread.java:3683)
- 01-31 14:36:04.864: I/dalvikvm(389): at java.lang.reflect.Method.invokeNative(Native Method)
- 01-31 14:36:04.874: I/dalvikvm(389): at java.lang.reflect.Method.invoke(Method.java:507)
- 01-31 14:36:04.874: I/dalvikvm(389): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
- 01-31 14:36:04.874: I/dalvikvm(389): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
- 01-31 14:36:04.874: I/dalvikvm(389): at dalvik.system.NativeStart.main(Native Method)
- 01-31 14:36:04.884: E/dalvikvm(389): VM aborting
4. JNIEnv 详解
(1) JNIEnv的C/C++声明
- struct _JNIEnv;
- struct _JavaVM;
- typedef const struct JNINativeInterface* C_JNIEnv;
- #if defined(__cplusplus) //为了兼容C 和 C++两种代码 使用该 宏加以区分
- typedef _JNIEnv JNIEnv; //C++ 中的JNIEnv类型
- typedef _JavaVM JavaVM;
- #else
- typedef const struct JNINativeInterface* JNIEnv;//C语言中的JNIEnv类型
- typedef const struct JNIInvokeInterface* JavaVM;
- #endif
(2) C语言中的JNIEnv
- /*
- * Table of interface function pointers.
- */
- struct JNINativeInterface {
- void* reserved0;
- void* reserved1;
- ... ...
- jboolean (*CallStaticBooleanMethodV)(JNIEnv*, jclass, jmethodID,
- va_list);
- jboolean (*CallStaticBooleanMethodA)(JNIEnv*, jclass, jmethodID,
- jvalue*);
- jbyte (*CallStaticByteMethod)(JNIEnv*, jclass, jmethodID, ...);
- jbyte (*CallStaticByteMethodV)(JNIEnv*, jclass, jmethodID, va_list);
- ... ...
- void* (*GetDirectBufferAddress)(JNIEnv*, jobject);
- jlong (*GetDirectBufferCapacity)(JNIEnv*, jobject);
- /* added in JNI 1.6 */
- jobjectRefType (*GetObjectRefType)(JNIEnv*, jobject);
- };
(3) C++中的JNIEnv
- /*
- * C++ object wrapper.
- *
- * This is usually overlaid on a C struct whose first element is a
- * JNINativeInterface*. We rely somewhat on compiler behavior.
- */
- struct _JNIEnv {
- /* do not rename this; it does not seem to be entirely opaque */
- const struct JNINativeInterface* functions;
- #if defined(__cplusplus)
- jint GetVersion()
- { return functions->GetVersion(this); }
- jlong GetDirectBufferCapacity(jobject buf)
- { return functions->GetDirectBufferCapacity(this, buf); }
- /* added in JNI 1.6 */
- jobjectRefType GetObjectRefType(jobject obj)
- { return functions->GetObjectRefType(this, obj); }
- #endif /*__cplusplus*/
- };
5. JNI方法命名规则(标准JNI规范)
6. JNI方法签名规则
Java类型 | 类型签名 |
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | J |
float | F |
double | D |
类 | L全限定类名 |
数组 | [元素类型签名 |
四. Java调用JNI法与日志打印
1. JNI数据类型
Java数据类型 | C本地类型 | JNI定义别名 |
int | long | jint/jsize |
long | __int64 | jlong |
byte | signed char | jbyte |
boolean | unsigned char | jboolean |
char | unsigned short | jchar |
short | short | jshort |
float | float | jfloat |
double | doyble | jdouble |
object' | _jobject | jobject |
- jobject (*CallObjectMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
- jboolean (*CallBooleanMethod)(JNIEnv*, jobject, jmethodID, ...);
- jboolean (*CallBooleanMethodV)(JNIEnv*, jobject, jmethodID, va_list);
- jboolean (*CallBooleanMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
- jbyte (*CallByteMethod)(JNIEnv*, jobject, jmethodID, ...);
- jbyte (*CallByteMethodV)(JNIEnv*, jobject, jmethodID, va_list);
- jbyte (*CallByteMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
- jchar (*CallCharMethod)(JNIEnv*, jobject, jmethodID, ...);
- jchar (*CallCharMethodV)(JNIEnv*, jobject, jmethodID, va_list);
- jchar (*CallCharMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
- jshort (*CallShortMethod)(JNIEnv*, jobject, jmethodID, ...);
- jshort (*CallShortMethodV)(JNIEnv*, jobject, jmethodID, va_list);
- jshort (*CallShortMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
- jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
- jint (*CallIntMethodV)(JNIEnv*, jobject, jmethodID, va_list);
- jint (*CallIntMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
- jlong (*CallLongMethod)(JNIEnv*, jobject, jmethodID, ...);
- jlong (*CallLongMethodV)(JNIEnv*, jobject, jmethodID, va_list);
- jbooleanArray (*NewBooleanArray)(JNIEnv*, jsize);
- jbyteArray (*NewByteArray)(JNIEnv*, jsize);
- jcharArray (*NewCharArray)(JNIEnv*, jsize);
- jshortArray (*NewShortArray)(JNIEnv*, jsize);
- jintArray (*NewIntArray)(JNIEnv*, jsize);
- jlongArray (*NewLongArray)(JNIEnv*, jsize);
- jfloatArray (*NewFloatArray)(JNIEnv*, jsize);
- jdoubleArray (*NewDoubleArray)(JNIEnv*, jsize);
2. JNI在Java和C语言之间传递int类型
- //将Java中的两个int值 传给C语言, 进行相加后, 返回java语言 shuliang.han.ndkparameterpassing.DataProvider
- public native int add(int x, int y);
C语言中定义的方法 :
- #include <jni.h>
- //方法签名, Java环境 和 调用native方法的类 必不可少, 后面的参数就是native方法的参数
- jint Java_shuliang_han_ndkparameterpassing_DataProvider_add(JNIEnv * env, jobject obj, jint x, jint y)
- {
- return x + y;
- }
使用NDK工具变异该c类库 :
3. NDK中C代码使用LogCat
(1) 引入头文件
- #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__)
头文件介绍 : log.h 是关于调用 LogCat日志文件;
- /*
- * Android log priority values, in ascending priority order. 日志等级
- */
- typedef enum android_LogPriority {
- ANDROID_LOG_UNKNOWN = 0,
- ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */
- ANDROID_LOG_VERBOSE,
- ANDROID_LOG_DEBUG,
- ANDROID_LOG_INFO,
- ANDROID_LOG_WARN,
- ANDROID_LOG_ERROR,
- ANDROID_LOG_FATAL,
- ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */
- } android_LogPriority;
- /*
- * Send a simple string to the log. 向LogCat中输出日志
- 参数介绍: 日志优先级 , 日志标签 , 日志内容
- */
- int __android_log_write(int prio, const char *tag, const char *text);
C语言中输入输出函数占位符介绍 :
占位符 | 数据类型 |
%d | int |
%ld | long int |
%c | char |
%f | float |
&lf | double |
%x | 十六进制 |
%O | 八进制 |
%s | 字符串 |
(2) Android.mk增加liblog.so动态库
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_MODULE := DataProvider
- LOCAL_SRC_FILES := DataProvider.c
- #增加log函数对应的函数库 liblog.so libthread_db.a
- LOCAL_LDLIBS += -llog -lthread_db
- include $(BUILD_SHARED_LIBRARY)
(3) 编译执行
- //Java中的int对应的是C语言中的long类型, 对应JNI中的jint类型, C语言中
- LOGI("JNI_日志 : x = %ld , y = %ld" , x , y);
最终的包含打印日志的完整代码 : 注意, 这里有一处可能错误, 如果是32位机器, int类型占位符使用 %d 即可;
- #include <jni.h>
- #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__)
- //方法签名, Java环境 和 调用native方法的类 必不可少, 后面的参数就是native方法的参数
- jint Java_shuliang_han_ndkparameterpassing_DataProvider_add(JNIEnv * env, jobject obj, jint x, jint y)
- {
- //Java中的int对应的是C语言中的long类型, 对应JNI中的jint类型, C语言中
- LOGI("JNI_日志 : x = %ld , y = %ld" , x , y);
- return x + y;
- }
重新编译C文件 : 执行 /android-ndk-r9c/ndk-build命令;
4. 字符串处理
- // java中的jstring, 转化为c的一个字符数组
- char* Jstring2CStr(JNIEnv* env, jstring jstr) {
- <span style="white-space:pre"> </span>//声明了一个字符串变量 rtn
- <span style="white-space:pre"> </span>char* rtn = NULL;
- <span style="white-space:pre"> </span>//找到Java中的String的Class对象
- <span style="white-space:pre"> </span>jclass clsstring = (*env)->FindClass(env, "java/lang/String");
- <span style="white-space:pre"> </span>//创建一个Java中的字符串 "GB2312"
- <span style="white-space:pre"> </span>jstring strencode = (*env)->NewStringUTF(env, "GB2312");
- <span style="white-space:pre"> </span>/*
- <span style="white-space:pre"> </span> * 获取String中定义的方法 getBytes(), 该方法的参数是 String类型的, 返回值是 byte[]数组
- <span style="white-space:pre"> </span> * "(Ljava/lang/String;)[B" 方法前面解析 :
- <span style="white-space:pre"> </span> * -- Ljava/lang/String; 表示参数是String字符串
- <span style="white-space:pre"> </span> * -- [B : 中括号表示这是一个数组, B代表byte类型, 返回值是一个byte数组
- <span style="white-space:pre"> </span> */
- <span style="white-space:pre"> </span>jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes",
- <span style="white-space:pre"> </span>"(Ljava/lang/String;)[B");
- <span style="white-space:pre"> </span>//调用Java中的getBytes方法, 传入参数介绍 参数②表示调用该方法的对象, 参数③表示方法id , 参数④表示方法参数
- <span style="white-space:pre"> </span>jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid,
- <span style="white-space:pre"> </span>strencode); // String .getByte("GB2312");
- <span style="white-space:pre"> </span>//获取数组的长度
- <span style="white-space:pre"> </span>jsize alen = (*env)->GetArrayLength(env, barr);
- <span style="white-space:pre"> </span>//获取数组中的所有的元素 , 存放在 jbyte*数组中
- <span style="white-space:pre"> </span>jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);
- <span style="white-space:pre"> </span>//将Java数组中所有元素拷贝到C的char*数组中, 注意C语言数组结尾要加一个 '\0'
- <span style="white-space:pre"> </span>if (alen > 0) {
- <span style="white-space:pre"> </span>rtn = (char*) malloc(alen + 1); //new char[alen+1]; "\0"
- <span style="white-space:pre"> </span>memcpy(rtn, ba, alen);
- <span style="white-space:pre"> </span>rtn[alen] = 0;
- <span style="white-space:pre"> </span>}
- <span style="white-space:pre"> </span>(*env)->ReleaseByteArrayElements(env, barr, ba, 0); //释放内存
- <span style="white-space:pre"> </span>return rtn;
- }
- 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");
- 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); //释放内存
.
作者 : 万境绝尘
转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/18964835
.
C语言方法 : 注意调用Jstring2CStr方法之后要强转, 否则会出错, Jstring2CStr方法要定义在该方法的前面, C语言中的方法要先声明才能使用;
- jstring Java_shuliang_han_ndkparameterpassing_DataProvider_sayHelloInc(JNIEnv *env, jobject obj, jstring str)
- {
- char *p = (char*)Jstring2CStr(env, str);
- //打印Java传递过来的数据
- LOGI("Java JNI string parameter is : %s", p);
- char *append = "append";
- //strcat(dest, source) 函数可以将source字符串 添加到dest字符串后面
- return (*env)->NewStringUTF(env, strcat(p, append));
- }
-- 如果没有强转会出现下面的错误 : char *p = Jstring2CStr(env, str);
-- 将Jstring2CStr方法定义在主方法下面会出现下面错误 :
- case R.id.sayHelloInc:
- Toast.makeText(getApplicationContext(), dataProvider.sayHelloInc("Hello"), Toast.LENGTH_LONG).show();
- break;
编译之后运行结果 :
5. 开发JNI程序流程
b. Java定义本地方法 : public native void LoginServer(String address, String user, String pwd);
注意跨语言字符串转换: JNI方法中, 要将Java的String字符串转为C中的char*字符串;
首先验证C码农提供的代码是否可用 : 验证该api是否可用, 在一个 int main() 函数中进行测试, 根据该测试代码查看方法执行相关的情况;
6. 数组参数处理
获取数组长度方法 : jni中定义 - jsize (*GetArrayLength)(JNIEnv*, jarray);
创建数组相关方法 :
- jbooleanArray (*NewBooleanArray)(JNIEnv*, jsize);
- jbyteArray (*NewByteArray)(JNIEnv*, jsize);
- jcharArray (*NewCharArray)(JNIEnv*, jsize);
- jshortArray (*NewShortArray)(JNIEnv*, jsize);
- jintArray (*NewIntArray)(JNIEnv*, jsize);
- jlongArray (*NewLongArray)(JNIEnv*, jsize);
- jfloatArray (*NewFloatArray)(JNIEnv*, jsize);
- jdoubleArray (*NewDoubleArray)(JNIEnv*, jsize);
获取数组元素相关方法 :
- jboolean* (*GetBooleanArrayElements)(JNIEnv*, jbooleanArray, jboolean*);
- jbyte* (*GetByteArrayElements)(JNIEnv*, jbyteArray, jboolean*);
- jchar* (*GetCharArrayElements)(JNIEnv*, jcharArray, jboolean*);
- jshort* (*GetShortArrayElements)(JNIEnv*, jshortArray, jboolean*);
- jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);
- jlong* (*GetLongArrayElements)(JNIEnv*, jlongArray, jboolean*);
- jfloat* (*GetFloatArrayElements)(JNIEnv*, jfloatArray, jboolean*);
- jdouble* (*GetDoubleArrayElements)(JNIEnv*, jdoubleArray, jboolean*);
C语言代码 :
- jintArray Java_shuliang_han_ndkparameterpassing_DataProvider_intMethod(JNIEnv *env, jobject obj, jintArray arr)
- {
- //获取arr大小
- int len = (*env)->GetArrayLength(env, arr);
- //在LogCat中打印出arr的大小
- LOGI("the length of array is %d", len);
- //如果长度为0, 返回arr
- if(len == 0)
- return arr;
- //如果长度大于0, 那么获取数组中的每个元素
- jint* p = (*env)->GetIntArrayElements(env, arr, 0);
- //打印出数组中每个元素的值
- int i = 0;
- for(; i < len; i ++)
- {
- LOGI("arr[%d] = %d", i, *(p + i));
- }
- return arr;
- }
- case R.id.intMethod:
- int[] array = {1, 2, 3, 4, 5};
- dataProvider.intMethod(array);
- break;
执行结果 : 上面的那种LogCat竟然启动失败, 只能将就着用这个了;
7. 本程序源码
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
- <Button
- android:id="@+id/add"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="调用 add 本地 方法"
- android:onClick="onClick"/>
- <Button
- android:id="@+id/sayHelloInc"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="调用 sayHelloInc 本地 方法"
- android:onClick="onClick"/>
- <Button
- android:id="@+id/intMethod"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="调用 intMethod 本地 方法"
- android:onClick="onClick"/>
- </LinearLayout>
Java源码 :
- package shuliang.han.ndkparameterpassing;
- import android.app.Activity;
- import android.os.Bundle;
- import android.view.View;
- import android.widget.Toast;
- public class MainActivity extends Activity {
- static{
- System.loadLibrary("DataProvider");
- }
- DataProvider dataProvider;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- dataProvider = new DataProvider();
- }
- public void onClick(View view) {
- int id = view.getId();
- switch (id) {
- case R.id.add:
- int result = dataProvider.add(1, 2);
- Toast.makeText(getApplicationContext(), "the add result : " + result, Toast.LENGTH_LONG).show();
- break;
- case R.id.sayHelloInc:
- Toast.makeText(getApplicationContext(), dataProvider.sayHelloInc("Hello"), Toast.LENGTH_LONG).show();
- break;
- case R.id.intMethod:
- int[] array = {1, 2, 3, 4, 5};
- dataProvider.intMethod(array);
- break;
- default:
- break;
- }
- }
- }
- package shuliang.han.ndkparameterpassing;
- public class DataProvider {
- //将Java中的两个int值 传给C语言, 进行相加后, 返回java语言 shuliang.han.ndkparameterpassing.DataProvider
- public native int add(int x, int y);
- //将Java字符串传递给C语言, C语言处理字符串之后, 将处理结果返回给java
- public native String sayHelloInc(String s);
- //将java中的int数组传递给C语言, C语言为每个元素加10, 返回给Java
- public native int[] intMethod(int[] nums);
- }
JNI相关源码 :
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_MODULE := DataProvider
- LOCAL_SRC_FILES := DataProvider.c
- #增加log函数对应的log库
- LOCAL_LDLIBS += -llog
- include $(BUILD_SHARED_LIBRARY)
- #include <jni.h>
- #include <string.h>
- #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__)
- // java中的jstring, 转化为c的一个字符数组
- char* Jstring2CStr(JNIEnv* env, jstring jstr) {
- <span style="white-space:pre"> </span>//声明了一个字符串变量 rtn
- <span style="white-space:pre"> </span>char* rtn = NULL;
- <span style="white-space:pre"> </span>//找到Java中的String的Class对象
- <span style="white-space:pre"> </span>jclass clsstring = (*env)->FindClass(env, "java/lang/String");
- <span style="white-space:pre"> </span>//创建一个Java中的字符串 "GB2312"
- <span style="white-space:pre"> </span>jstring strencode = (*env)->NewStringUTF(env, "GB2312");
- <span style="white-space:pre"> </span>/*
- <span style="white-space:pre"> </span> * 获取String中定义的方法 getBytes(), 该方法的参数是 String类型的, 返回值是 byte[]数组
- <span style="white-space:pre"> </span> * "(Ljava/lang/String;)[B" 方法前面解析 :
- <span style="white-space:pre"> </span> * -- Ljava/lang/String; 表示参数是String字符串
- <span style="white-space:pre"> </span> * -- [B : 中括号表示这是一个数组, B代表byte类型, 返回值是一个byte数组
- <span style="white-space:pre"> </span> */
- <span style="white-space:pre"> </span>jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes",
- <span style="white-space:pre"> </span>"(Ljava/lang/String;)[B");
- <span style="white-space:pre"> </span>//调用Java中的getBytes方法, 传入参数介绍 参数②表示调用该方法的对象, 参数③表示方法id , 参数④表示方法参数
- <span style="white-space:pre"> </span>jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid,
- <span style="white-space:pre"> </span>strencode); // String .getByte("GB2312");
- <span style="white-space:pre"> </span>//获取数组的长度
- <span style="white-space:pre"> </span>jsize alen = (*env)->GetArrayLength(env, barr);
- <span style="white-space:pre"> </span>//获取数组中的所有的元素 , 存放在 jbyte*数组中
- <span style="white-space:pre"> </span>jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);
- <span style="white-space:pre"> </span>//将Java数组中所有元素拷贝到C的char*数组中, 注意C语言数组结尾要加一个 '\0'
- <span style="white-space:pre"> </span>if (alen > 0) {
- <span style="white-space:pre"> </span>rtn = (char*) malloc(alen + 1); //new char[alen+1]; "\0"
- <span style="white-space:pre"> </span>memcpy(rtn, ba, alen);
- <span style="white-space:pre"> </span>rtn[alen] = 0;
- <span style="white-space:pre"> </span>}
- <span style="white-space:pre"> </span>(*env)->ReleaseByteArrayElements(env, barr, ba, 0); //释放内存
- <span style="white-space:pre"> </span>return rtn;
- }
- //方法签名, Java环境 和 调用native方法的类 必不可少, 后面的参数就是native方法的参数
- jint Java_shuliang_han_ndkparameterpassing_DataProvider_add(JNIEnv * env, jobject obj, jint x, jint y)
- {
- //Java中的int对应的是C语言中的long类型, 对应JNI中的jint类型, C语言中
- LOGI("JNI_log : x = %d , y = %d" , x , y);
- return x + y;
- }
- jstring Java_shuliang_han_ndkparameterpassing_DataProvider_sayHelloInc(JNIEnv *env, jobject obj, jstring str)
- {
- char *p = (char*)Jstring2CStr(env, str);
- //打印Java传递过来的数据
- LOGI("Java JNI string parameter is : %s", p);
- char *append = "append";
- //strcat(dest, source) 函数可以将source字符串 添加到dest字符串后面
- return (*env)->NewStringUTF(env, strcat(p, append));
- }
- jintArray Java_shuliang_han_ndkparameterpassing_DataProvider_intMethod(JNIEnv *env, jobject obj, jintArray arr)
- {
- //获取arr大小
- int len = (*env)->GetArrayLength(env, arr);
- //在LogCat中打印出arr的大小
- LOGI("the length of array is %d", len);
- //如果长度为0, 返回arr
- if(len == 0)
- return arr;
- //如果长度大于0, 那么获取数组中的每个元素
- jint* p = (*env)->GetIntArrayElements(env, arr, 0);
- //打印出数组中每个元素的值
- int i = 0;
- for(; i < len; i ++)
- {
- LOGI("arr[%d] = %d", i, *(p + i));
- }
- return arr;
- }
8. 上传代码到GitHub
创建新项目 : han1202012/NDKParameterPassing ;
-- SSH地址 : git@github.com:han1202012/NDKParameterPassing.git ;
-- HTTP地址 : https://github.com/han1202012/NDKParameterPassing.git ;
五. C语言代码回调Java方法
1. C代码回调Java方法的流程
(1) 找到java对应的Class
- //DataProvider完整类名 shulaing.han.ndk_callback.DataProvider
- char* classname = "shulaing/han/ndk_callback/DataProvider";
- jclass dpclazz = (*env)->FindClass(env, classname);
(2) 找到要调用的方法的methodID
- //参数介绍 : 第二个参数是Class对象, 第三个参数是方法名,第四个参数是方法的签名, 获取到调用的method
- jmethodID methodID = (*env)->GetMethodID(env, dpclazz, "Add", "(II)I");
- jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig)
- { return functions->GetStaticMethodID(this, clazz, name, sig); }
(3) 在C语言中调用相应方法
- jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
- jobject (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...);
- jobject (*CallObjectMethodV)(JNIEnv*, jobject, jmethodID, va_list);
- jobject (*CallObjectMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
- jboolean (*CallBooleanMethod)(JNIEnv*, jobject, jmethodID, ...);
- jboolean (*CallBooleanMethodV)(JNIEnv*, jobject, jmethodID, va_list);
- jboolean (*CallBooleanMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
- jbyte (*CallByteMethod)(JNIEnv*, jobject, jmethodID, ...);
- jbyte (*CallByteMethodV)(JNIEnv*, jobject, jmethodID, va_list);
- jbyte (*CallByteMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
- jchar (*CallCharMethod)(JNIEnv*, jobject, jmethodID, ...);
- jchar (*CallCharMethodV)(JNIEnv*, jobject, jmethodID, va_list);
- jchar (*CallCharMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
- jshort (*CallShortMethod)(JNIEnv*, jobject, jmethodID, ...);
- jshort (*CallShortMethodV)(JNIEnv*, jobject, jmethodID, va_list);
- jshort (*CallShortMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
- jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
- jint (*CallIntMethodV)(JNIEnv*, jobject, jmethodID, va_list);
- jint (*CallIntMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
- jlong (*CallLongMethod)(JNIEnv*, jobject, jmethodID, ...);
- jlong (*CallLongMethodV)(JNIEnv*, jobject, jmethodID, va_list);
- jlong (*CallLongMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
- jfloat (*CallFloatMethod)(JNIEnv*, jobject, jmethodID, ...) __NDK_FPABI__;
- jfloat (*CallFloatMethodV)(JNIEnv*, jobject, jmethodID, va_list) __NDK_FPABI__;
- jfloat (*CallFloatMethodA)(JNIEnv*, jobject, jmethodID, jvalue*) __NDK_FPABI__;
- jdouble (*CallDoubleMethod)(JNIEnv*, jobject, jmethodID, ...) __NDK_FPABI__;
- jdouble (*CallDoubleMethodV)(JNIEnv*, jobject, jmethodID, va_list) __NDK_FPABI__;
- jdouble (*CallDoubleMethodA)(JNIEnv*, jobject, jmethodID, jvalue*) __NDK_FPABI__;
- void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
- void (*CallVoidMethodV)(JNIEnv*, jobject, jmethodID, va_list);
- void (*CallVoidMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
静态方法 : CallStaticTypeMethod, 其中的Type随着返回值类型不同而改变;
2. 一些基本代码编写
- package shulaing.han.ndk_callback;
- public class DataProvider {
- public native void callCcode();
- //C调用java中空方法 shulaing.han.ndk_callback.DataProvider
- public void helloFromJava(){
- System.out.println("hello from java");
- }
- //C调用java中的带两个int参数的方法
- public int Add(int x,int y){
- return x + y;
- }
- //C调用java中参数为string的方法
- public void printString(String s){
- System.out.println(s);
- }
- }
生成头文件 : 进入 bin/classed目录, 使用 javah shulaing.han.ndk_callback.DataProvider 命令, 可以在bin/classes下生成头文件;
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- /* Header for class shulaing_han_ndk_callback_DataProvider */
- #ifndef _Included_shulaing_han_ndk_callback_DataProvider
- #define _Included_shulaing_han_ndk_callback_DataProvider
- #ifdef __cplusplus
- extern "C" {
- #endif
- /*
- * Class: shulaing_han_ndk_callback_DataProvider
- * Method: callCcode
- * Signature: ()V
- */
- JNIEXPORT void JNICALL Java_shulaing_han_ndk_1callback_DataProvider_callCcode
- (JNIEnv *, jobject);
- #ifdef __cplusplus
- }
- #endif
- #endif
编写Android.mk文件 : 注意将LogCat日志输出系统动态库加入;
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_MODULE := jni
- LOCAL_SRC_FILES := jni.c
- #增加log函数对应的log库
- LOCAL_LDLIBS += -llog
- include $(BUILD_SHARED_LIBRARY)
编写jni的C代码 : 注意加入LogCat相关导入的包;
- #include "shulaing_han_ndk_callback_DataProvider.h"
- #include <string.h>
- #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__)
3. C中回调Java的void返回值方法
- $ javap -s shulaing.han.ndk_callback.DataProvider
- Compiled from "DataProvider.java"
- public class shulaing.han.ndk_callback.DataProvider extends java.lang.Object{
- public shulaing.han.ndk_callback.DataProvider();
- Signature: ()V
- public native void callCcode();
- Signature: ()V
- public void helloFromJava();
- Signature: ()V
- public int Add(int, int);
- Signature: (II)I
- public void printString(java.lang.String);
- Signature: (Ljava/lang/String;)V
- }
- jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
- jobject (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...);
- jobject (*CallObjectMethodV)(JNIEnv*, jobject, jmethodID, va_list);
- jobject (*CallObjectMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
- jboolean (*CallBooleanMethod)(JNIEnv*, jobject, jmethodID, ...);
- jboolean (*CallBooleanMethodV)(JNIEnv*, jobject, jmethodID, va_list);
- jboolean (*CallBooleanMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
- jbyte (*CallByteMethod)(JNIEnv*, jobject, jmethodID, ...);
- jbyte (*CallByteMethodV)(JNIEnv*, jobject, jmethodID, va_list);
- jbyte (*CallByteMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
- jchar (*CallCharMethod)(JNIEnv*, jobject, jmethodID, ...);
- jchar (*CallCharMethodV)(JNIEnv*, jobject, jmethodID, va_list);
- jchar (*CallCharMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
- jshort (*CallShortMethod)(JNIEnv*, jobject, jmethodID, ...);
- jshort (*CallShortMethodV)(JNIEnv*, jobject, jmethodID, va_list);
- jshort (*CallShortMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
- jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
- jint (*CallIntMethodV)(JNIEnv*, jobject, jmethodID, va_list);
- jint (*CallIntMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
- jlong (*CallLongMethod)(JNIEnv*, jobject, jmethodID, ...);
- jlong (*CallLongMethodV)(JNIEnv*, jobject, jmethodID, va_list);
- jlong (*CallLongMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
- jfloat (*CallFloatMethod)(JNIEnv*, jobject, jmethodID, ...) __NDK_FPABI__;
- jfloat (*CallFloatMethodV)(JNIEnv*, jobject, jmethodID, va_list) __NDK_FPABI__;
- jfloat (*CallFloatMethodA)(JNIEnv*, jobject, jmethodID, jvalue*) __NDK_FPABI__;
- jdouble (*CallDoubleMethod)(JNIEnv*, jobject, jmethodID, ...) __NDK_FPABI__;
- jdouble (*CallDoubleMethodV)(JNIEnv*, jobject, jmethodID, va_list) __NDK_FPABI__;
- jdouble (*CallDoubleMethodA)(JNIEnv*, jobject, jmethodID, jvalue*) __NDK_FPABI__;
- void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
- void (*CallVoidMethodV)(JNIEnv*, jobject, jmethodID, va_list);
- void (*CallVoidMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
- #include "shulaing_han_ndk_callback_DataProvider.h"
- #include <string.h>
- #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__)
- JNIEXPORT void JNICALL Java_shulaing_han_ndk_1callback_DataProvider_callCcode
- (JNIEnv * env, jobject obj)
- {
- //调用DataProvider对象中的helloFromJava()方法
- //获取到某个对象, 获取对象中的方法, 调用获取到的方法
- LOGI("in code");
- //DataProvider完整类名 shulaing.han.ndk_callback.DataProvider
- char* classname = "shulaing/han/ndk_callback/DataProvider";
- jclass dpclazz = (*env)->FindClass(env, classname);
- if(dpclazz == 0)
- LOGI("class not find !!!");
- else
- LOGI("class find !!!");
- //参数介绍 : 第二个参数是Class对象, 第三个参数是方法名,第四个参数是方法的签名, 获取到调用的method
- jmethodID methodID = (*env)->GetMethodID(env, dpclazz, "helloFromJava", "()V");
- if(methodID == 0)
- LOGI("method not find !!!");
- else
- LOGI("method find !!!");
- /*
- * 调用方法 void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
- * 参数介绍 : 后面的 ... 是可变参数, 如果该返回值void的方法有参数, 就将参数按照次序排列
- */
- LOGI("before call method");
- (*env)->CallVoidMethod(env, obj, methodID);
- LOGI("after call method");
- }
Java代码 :
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:paddingBottom="@dimen/activity_vertical_margin"
- android:paddingLeft="@dimen/activity_horizontal_margin"
- android:paddingRight="@dimen/activity_horizontal_margin"
- android:paddingTop="@dimen/activity_vertical_margin"
- tools:context=".MainActivity" >
- <Button
- android:id="@+id/call_void_method"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:onClick="onClick"
- android:text="C语言回调Java中的空方法" />
- </LinearLayout>
- package shulaing.han.ndk_callback;
- import android.app.Activity;
- import android.os.Bundle;
- import android.view.View;
- public class MainActivity extends Activity {
- static{
- System.loadLibrary("jni");
- }
- DataProvider dp;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- dp = new DataProvider();
- }
- public void onClick(View view) {
- int id = view.getId();
- switch (id) {
- case R.id.call_void_method:
- dp.callCcode();
- break;
- default:
- break;
- }
- }
- }
.
4. C代码回调Java中带String参数的方法
- public native void callCcode();
- public native void callCcode1();
- public native void callCcode2();
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- /* Header for class shulaing_han_ndk_callback_DataProvider */
- #ifndef _Included_shulaing_han_ndk_callback_DataProvider
- #define _Included_shulaing_han_ndk_callback_DataProvider
- #ifdef __cplusplus
- extern "C" {
- #endif
- /*
- * Class: shulaing_han_ndk_callback_DataProvider
- * Method: callCcode
- * Signature: ()V
- */
- JNIEXPORT void JNICALL Java_shulaing_han_ndk_1callback_DataProvider_callCcode
- (JNIEnv *, jobject);
- /*
- * Class: shulaing_han_ndk_callback_DataProvider
- * Method: callCcode1
- * Signature: ()V
- */
- JNIEXPORT void JNICALL Java_shulaing_han_ndk_1callback_DataProvider_callCcode1
- (JNIEnv *, jobject);
- /*
- * Class: shulaing_han_ndk_callback_DataProvider
- * Method: callCcode2
- * Signature: ()V
- */
- JNIEXPORT void JNICALL Java_shulaing_han_ndk_1callback_DataProvider_callCcode2
- (JNIEnv *, jobject);
- #ifdef __cplusplus
- }
- #endif
- #endif
- JNIEXPORT void JNICALL Java_shulaing_han_ndk_1callback_DataProvider_callCcode1
- (JNIEnv *env, jobject obj)
- {
- //调用DataProvider对象中的helloFromJava()方法
- //获取到某个对象, 获取对象中的方法, 调用获取到的方法
- LOGI("in code");
- //DataProvider完整类名 shulaing.han.ndk_callback.DataProvider
- char* classname = "shulaing/han/ndk_callback/DataProvider";
- jclass dpclazz = (*env)->FindClass(env, classname);
- if(dpclazz == 0)
- LOGI("class not find !!!");
- else
- LOGI("class find !!!");
- //参数介绍 : 第二个参数是Class对象, 第三个参数是方法名,第四个参数是方法的签名, 获取到调用的method
- jmethodID methodID = (*env)->GetMethodID(env, dpclazz, "printString", "(Ljava/lang/String;)V");
- if(methodID == 0)
- LOGI("method not find !!!");
- else
- LOGI("method find !!!");
- /*
- * 调用方法 void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
- * 参数介绍 : 后面的 ... 是可变参数, 如果该返回值void的方法有参数, 就将参数按照次序排列
- */
- LOGI("before call method");
- (*env)->CallVoidMethod(env, obj, methodID, (*env)->NewStringUTF(env, "printString method callback success!!"));
- LOGI("after call method");
- }
执行后的结果 :
5. C代码中回调带两个int类型的参数的方法
- JNIEXPORT void JNICALL Java_shulaing_han_ndk_1callback_DataProvider_callCcode2
- (JNIEnv *env, jobject obj)
- {
- //调用DataProvider对象中的helloFromJava()方法
- //获取到某个对象, 获取对象中的方法, 调用获取到的方法
- LOGI("in code");
- //DataProvider完整类名 shulaing.han.ndk_callback.DataProvider
- char* classname = "shulaing/han/ndk_callback/DataProvider";
- jclass dpclazz = (*env)->FindClass(env, classname);
- if(dpclazz == 0)
- LOGI("class not find !!!");
- else
- LOGI("class find !!!");
- //参数介绍 : 第二个参数是Class对象, 第三个参数是方法名,第四个参数是方法的签名, 获取到调用的method
- jmethodID methodID = (*env)->GetMethodID(env, dpclazz, "Add", "(II)I");
- if(methodID == 0)
- LOGI("method not find !!!");
- else
- LOGI("method find !!!");
- /*
- * 调用方法 void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
- * 参数介绍 : 后面的 ... 是可变参数, 如果该返回值void的方法有参数, 就将参数按照次序排列
- */
- LOGI("before call method");
- (*env)->CallIntMethod(env, obj, methodID, 3, 5);
- LOGI("after call method");
- }
Java代码 :
- case R.id.call_int_parameter_method:
- dp.callCcode2();
- break;
.
作者 : 万境绝尘
转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/18964835
.
6. 完整源码
- package shulaing.han.ndk_callback;
- public class DataProvider {
- public native void callCcode();
- public native void callCcode1();
- public native void callCcode2();
- //C调用java中空方法 shulaing.han.ndk_callback.DataProvider
- public void helloFromJava(){
- System.out.println("hello from java");
- }
- //C调用java中的带两个int参数的方法
- public int Add(int x,int y){
- System.out.println("the add result is : " + (x + y));
- return x + y;
- }
- //C调用java中参数为string的方法
- public void printString(String s){
- System.out.println("in java code :" + s);
- }
- }
- package shulaing.han.ndk_callback;
- import android.app.Activity;
- import android.os.Bundle;
- import android.view.View;
- public class MainActivity extends Activity {
- static{
- System.loadLibrary("jni");
- }
- DataProvider dp;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- dp = new DataProvider();
- }
- public void onClick(View view) {
- int id = view.getId();
- switch (id) {
- case R.id.call_void_method:
- dp.callCcode();
- break;
- case R.id.call_string_parameter_method:
- dp.callCcode1();
- break;
- case R.id.call_int_parameter_method:
- dp.callCcode2();
- break;
- default:
- break;
- }
- }
- }
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:paddingBottom="@dimen/activity_vertical_margin"
- android:paddingLeft="@dimen/activity_horizontal_margin"
- android:paddingRight="@dimen/activity_horizontal_margin"
- android:paddingTop="@dimen/activity_vertical_margin"
- tools:context=".MainActivity" >
- <Button
- android:id="@+id/call_void_method"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:onClick="onClick"
- android:text="C语言回调Java中的空方法" />
- <Button
- android:id="@+id/call_string_parameter_method"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:onClick="onClick"
- android:text="C语言回调Java中的String参数方法" />
- <Button
- android:id="@+id/call_int_parameter_method"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:onClick="onClick"
- android:text="C语言回调Java中的int参数方法" />
- </LinearLayout>
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- /* Header for class shulaing_han_ndk_callback_DataProvider */
- #ifndef _Included_shulaing_han_ndk_callback_DataProvider
- #define _Included_shulaing_han_ndk_callback_DataProvider
- #ifdef __cplusplus
- extern "C" {
- #endif
- /*
- * Class: shulaing_han_ndk_callback_DataProvider
- * Method: callCcode
- * Signature: ()V
- */
- JNIEXPORT void JNICALL Java_shulaing_han_ndk_1callback_DataProvider_callCcode
- (JNIEnv *, jobject);
- /*
- * Class: shulaing_han_ndk_callback_DataProvider
- * Method: callCcode1
- * Signature: ()V
- */
- JNIEXPORT void JNICALL Java_shulaing_han_ndk_1callback_DataProvider_callCcode1
- (JNIEnv *, jobject);
- /*
- * Class: shulaing_han_ndk_callback_DataProvider
- * Method: callCcode2
- * Signature: ()V
- */
- JNIEXPORT void JNICALL Java_shulaing_han_ndk_1callback_DataProvider_callCcode2
- (JNIEnv *, jobject);
- #ifdef __cplusplus
- }
- #endif
- #endif
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_MODULE := jni
- LOCAL_SRC_FILES := jni.c
- #增加log函数对应的log库
- LOCAL_LDLIBS += -llog
- include $(BUILD_SHARED_LIBRARY)
- #include "shulaing_han_ndk_callback_DataProvider.h"
- #include "first.h"
- #include <string.h>
- #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__)
- JNIEXPORT void JNICALL Java_shulaing_han_ndk_1callback_DataProvider_callCcode
- (JNIEnv * env, jobject obj)
- {
- //调用DataProvider对象中的helloFromJava()方法
- //获取到某个对象, 获取对象中的方法, 调用获取到的方法
- LOGI("in code");
- //DataProvider完整类名 shulaing.han.ndk_callback.DataProvider
- char* classname = "shulaing/han/ndk_callback/DataProvider";
- jclass dpclazz = (*env)->FindClass(env, classname);
- if(dpclazz == 0)
- LOGI("class not find !!!");
- else
- LOGI("class find !!!");
- //参数介绍 : 第二个参数是Class对象, 第三个参数是方法名,第四个参数是方法的签名, 获取到调用的method
- jmethodID methodID = (*env)->GetMethodID(env, dpclazz, "helloFromJava", "()V");
- if(methodID == 0)
- LOGI("method not find !!!");
- else
- LOGI("method find !!!");
- /*
- * 调用方法 void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
- * 参数介绍 : 后面的 ... 是可变参数, 如果该返回值void的方法有参数, 就将参数按照次序排列
- */
- LOGI("before call method");
- (*env)->CallVoidMethod(env, obj, methodID);
- LOGI("after call method");
- }
- JNIEXPORT void JNICALL Java_shulaing_han_ndk_1callback_DataProvider_callCcode1
- (JNIEnv *env, jobject obj)
- {
- //调用DataProvider对象中的helloFromJava()方法
- //获取到某个对象, 获取对象中的方法, 调用获取到的方法
- LOGI("in code");
- //DataProvider完整类名 shulaing.han.ndk_callback.DataProvider
- char* classname = "shulaing/han/ndk_callback/DataProvider";
- jclass dpclazz = (*env)->FindClass(env, classname);
- if(dpclazz == 0)
- LOGI("class not find !!!");
- else
- LOGI("class find !!!");
- //参数介绍 : 第二个参数是Class对象, 第三个参数是方法名,第四个参数是方法的签名, 获取到调用的method
- jmethodID methodID = (*env)->GetMethodID(env, dpclazz, "printString", "(Ljava/lang/String;)V");
- if(methodID == 0)
- LOGI("method not find !!!");
- else
- LOGI("method find !!!");
- /*
- * 调用方法 void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
- * 参数介绍 : 后面的 ... 是可变参数, 如果该返回值void的方法有参数, 就将参数按照次序排列
- */
- LOGI("before call method");
- (*env)->CallVoidMethod(env, obj, methodID, (*env)->NewStringUTF(env, "printString method callback success!!"));
- LOGI("after call method");
- }
- /*
- * 实际开发的情况
- * C代码工程师给我们 first.h first.c , 我们只需要将first.h引入, 然后就可以使用其中的方法了
- */
- JNIEXPORT void JNICALL Java_shulaing_han_ndk_1callback_DataProvider_callCcode2
- (JNIEnv *env, jobject obj)
- {
- //调用DataProvider对象中的helloFromJava()方法
- //获取到某个对象, 获取对象中的方法, 调用获取到的方法
- LOGI("in code");
- //DataProvider完整类名 shulaing.han.ndk_callback.DataProvider
- char* classname = "shulaing/han/ndk_callback/DataProvider";
- jclass dpclazz = (*env)->FindClass(env, classname);
- if(dpclazz == 0)
- LOGI("class not find !!!");
- else
- LOGI("class find !!!");
- //参数介绍 : 第二个参数是Class对象, 第三个参数是方法名,第四个参数是方法的签名, 获取到调用的method
- jmethodID methodID = (*env)->GetMethodID(env, dpclazz, "Add", "(II)I");
- if(methodID == 0)
- LOGI("method not find !!!");
- else
- LOGI("method find !!!");
- /*
- * 调用方法 void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
- * 参数介绍 : 后面的 ... 是可变参数, 如果该返回值void的方法有参数, 就将参数按照次序排列
- */
- LOGI("before call method");
- (*env)->CallIntMethod(env, obj, methodID, 3, 5);
- LOGI("after call method");
- }
7. 将程序上传到GitHub中
六. 实际开发中的环境
- #ifndef FIRST_H
- #define FIRST_H
- extern int first(int x, int y);
- #endif /* FIRST_H */
first.c源码 :
- #include "first.h"
- int first(int x, int y)
- {
- return x + y;
- }
在签名函数中, 直接调用 first()方法即可;
七 分析Log日志系统框架的JNI代码
1. 分析Log.java源码
- package android.util;
- import com.android.internal.os.RuntimeInit;
- import java.io.PrintWriter;
- import java.io.StringWriter;
- public final class Log {
- ... ...
- //打印日志
- public static int d(String tag, String msg) {
- return println_native(LOG_ID_MAIN, DEBUG, tag, msg);
- }
- //打印日志和异常
- public static int d(String tag, String msg, Throwable tr) {
- return println_native(LOG_ID_MAIN, DEBUG, tag, msg + '\n' + getStackTraceString(tr));
- }
- //打印日志
- public static int i(String tag, String msg) {
- return println_native(LOG_ID_MAIN, INFO, tag, msg);
- }
- ... ...
- //声明native方法
- public static native boolean isLoggable(String tag, int level);
- ... ...
- /** @hide */ public static final int LOG_ID_MAIN = 0;
- /** @hide */ public static final int LOG_ID_RADIO = 1;
- /** @hide */ public static final int LOG_ID_EVENTS = 2;
- /** @hide */ public static final int LOG_ID_SYSTEM = 3;
- //声明native方法
- /** @hide */ public static native int println_native(int bufID,
- int priority, String tag, String msg);
- }
2. 分析Log系统JNI层源码
- #define LOG_NAMESPACE "log.tag."
- #define LOG_TAG "Log_println"
- #include <assert.h>
- #include <cutils/properties.h>
- #include <utils/Log.h>
- #include <utils/String8.h>
- #include "jni.h"
- #include "utils/misc.h"
- #include "android_runtime/AndroidRuntime.h"
- ... ...
- namespace android {
- struct levels_t {
- jint verbose;
- jint debug;
- jint info;
- jint warn;
- jint error;
- jint assert;
- };
- static levels_t levels;
- static int toLevel(const char* value)
- {
- switch (value[0]) {
- case 'V': return levels.verbose;
- case 'D': return levels.debug;
- case 'I': return levels.info;
- case 'W': return levels.warn;
- case 'E': return levels.error;
- case 'A': return levels.assert;
- case 'S': return -1; // SUPPRESS
- }
- return levels.info;
- }
- /*
- 实现Java层声明的 isLoggable 方法, 注意方法名不符合标准JNI规范
- 标准的JNI规范方法名应该是 Java_包名_类名_方法名
- 其中传入了JNIEnv 和 jobject 参数, JNIEnv参数是Java运行环境, 可以与JVM进行交互
- jobject参数是包含Native方法的Java类对象
- 该方法中可以通过JNIEnv调用本地库进行函数处理, 最后返回给Java层函数
- */
- static jboolean android_util_Log_isLoggable(JNIEnv* env, jobject clazz, jstring tag, jint level)
- {
- #ifndef HAVE_ANDROID_OS
- return false;
- #else /* HAVE_ANDROID_OS */
- int len;
- char key[PROPERTY_KEY_MAX];
- char buf[PROPERTY_VALUE_MAX];
- if (tag == NULL) {
- return false;
- }
- jboolean result = false;
- //调用了JNI函数
- const char* chars = env->GetStringUTFChars(tag, NULL);
- if ((strlen(chars)+sizeof(LOG_NAMESPACE)) > PROPERTY_KEY_MAX) {
- jclass clazz = env->FindClass("java/lang/IllegalArgumentException");
- char buf2[200];
- snprintf(buf2, sizeof(buf2), "Log tag \"%s\" exceeds limit of %d characters\n",
- chars, PROPERTY_KEY_MAX - sizeof(LOG_NAMESPACE));
- // release the chars!
- env->ReleaseStringUTFChars(tag, chars);
- env->ThrowNew(clazz, buf2);
- return false;
- } else {
- strncpy(key, LOG_NAMESPACE, sizeof(LOG_NAMESPACE)-1);
- strcpy(key + sizeof(LOG_NAMESPACE) - 1, chars);
- }
- env->ReleaseStringUTFChars(tag, chars);
- len = property_get(key, buf, "");
- int logLevel = toLevel(buf);
- return (logLevel >= 0 && level >= logLevel) ? true : false;
- #endif /* HAVE_ANDROID_OS */
- }
- /*
- * In class android.util.Log:
- * public static native int println_native(int buffer, int priority, String tag, String msg)
- */
- static jint android_util_Log_println_native(JNIEnv* env, jobject clazz,
- jint bufID, jint priority, jstring tagObj, jstring msgObj)
- {
- const char* tag = NULL;
- const char* msg = NULL;
- if (msgObj == NULL) {
- jclass npeClazz;
- npeClazz = env->FindClass("java/lang/NullPointerException");
- assert(npeClazz != NULL);
- env->ThrowNew(npeClazz, "println needs a message");
- return -1;
- }
- if (bufID < 0 || bufID >= LOG_ID_MAX) {
- jclass npeClazz;
- npeClazz = env->FindClass("java/lang/NullPointerException");
- assert(npeClazz != NULL);
- env->ThrowNew(npeClazz, "bad bufID");
- return -1;
- }
- if (tagObj != NULL)
- tag = env->GetStringUTFChars(tagObj, NULL); //调用JNI函数
- msg = env->GetStringUTFChars(msgObj, NULL);
- int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);
- if (tag != NULL)
- env->ReleaseStringUTFChars(tagObj, tag); //调用JNI函数释放资源
- env->ReleaseStringUTFChars(msgObj, msg); //调用JNI函数释放资源
- return res;
- }
- /*
- * JNI registration. JNI方法注册
- */
- static JNINativeMethod gMethods[] = {
- /* name, signature, funcPtr */
- { "isLoggable", "(Ljava/lang/String;I)Z", (void*) android_util_Log_isLoggable },
- { "println_native", "(IILjava/lang/String;Ljava/lang/String;)I", (void*) android_util_Log_println_native },
- };
- int register_android_util_Log(JNIEnv* env)
- {
- jclass clazz = env->FindClass("android/util/Log");
- if (clazz == NULL) {
- LOGE("Can't find android/util/Log");
- return -1;
- }
- levels.verbose = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "VERBOSE", "I"));
- levels.debug = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "DEBUG", "I"));
- levels.info = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "INFO", "I"));
- levels.warn = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "WARN", "I"));
- levels.error = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "ERROR", "I"));
- levels.assert = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "ASSERT", "I"));
- return AndroidRuntime::registerNativeMethods(env, "android/util/Log", gMethods, NELEM(gMethods));
- }
- }; // namespace android
3. 声明JNI 与 Native 方法的映射关系
- typedef struct {
- const char* name; //Java层Native函数方法名
- const char* signature; //Java层Native函数的签名
- void* fnPtr; //JNI层实现的方法
- } JNINativeMethod;
- /*
- * JNI registration.
- */
- static JNINativeMethod gMethods[] = {
- /* name, signature, funcPtr */
- { "isLoggable", "(Ljava/lang/String;I)Z", (void*) android_util_Log_isLoggable },
- { "println_native", "(IILjava/lang/String;Ljava/lang/String;)I", (void*) android_util_Log_println_native },
- };
JNINativeMethod结构体作用 : JNINativeMethod是一个结构体类型, 声明了Native方法 与 JNI方法 的映射关系;
4. 注册JNI方法到虚拟机中
- int register_android_util_Log(JNIEnv* env)
- {
- jclass clazz = env->FindClass("android/util/Log");
- if (clazz == NULL) {
- LOGE("Can't find android/util/Log");
- return -1;
- }
- levels.verbose = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "VERBOSE", "I"));
- levels.debug = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "DEBUG", "I"));
- levels.info = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "INFO", "I"));
- levels.warn = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "WARN", "I"));
- levels.error = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "ERROR", "I"));
- levels.assert = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "ASSERT", "I"));
- return AndroidRuntime::registerNativeMethods(env, "android/util/Log", gMethods, NELEM(gMethods));
- }
- }; // namespace android
核心方法 : 该函数调用了 AndroidRuntime::registerNativeMethods(env, "android/util/Log", gMethods, NELEM(gMethods)) 方法注册JNI方法;
5. 解析registerNativeMethod函数
- /*
- * Register native methods using JNI.
- */
- /*static*/ int AndroidRuntime::registerNativeMethods(JNIEnv* env,
- const char* className, const JNINativeMethod* gMethods, int numMethods)
- {
- return jniRegisterNativeMethods(env, className, gMethods, numMethods);
- }
registerNativeMethods 方法只是对 jniRegisterNativeMethods 方法的封装, 在JNIHelp.h中找到该方法的声明:
- /*
- * Register one or more native methods with a particular class.
- */
- int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
- const JNINativeMethod* gMethods, int numMethods);
- /*
- * Register native JNI-callable methods.
- *
- * "className" looks like "java/lang/String".
- */
- int jniRegisterNativeMethods(JNIEnv* env, const char* className,
- const JNINativeMethod* gMethods, int numMethods)
- {
- jclass clazz;
- LOGV("Registering %s natives\n", className);
- clazz = (*env)->FindClass(env, className);
- if (clazz == NULL) {
- LOGE("Native registration unable to find class '%s'\n", className);
- return -1;
- }
- int result = 0;
- if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
- LOGE("RegisterNatives failed for '%s'\n", className);
- result = -1;
- }
- (*env)->DeleteLocalRef(env, clazz);
- return result;
- }
6. JNI的规范
这篇关于NDK开发基本知识的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!