环境搭建好之后,来写写第一小例子,调用一个无参的Java native方法,大部分的JNI开发编写流程都类似,所以这个小例子理解以后,其他的例子流程上就大致熟悉了。我总结了大致分为这么几步:
一、编写java调用的本地方法。这个一般根据业务需求来写。
<span style="font-size:14px;">public native String getMessageFromC();</span>
二、编写C代码
创建jni 目录,(注意必须叫 这个名字),并在这个目录下创建xxx.c文件 ,写入c代码即可。<span style="font-size:14px;">#include <stdio.h>
#include <jni.h>
//这行 可以通过javah命令来生成,最好通过此命令来生成,免得写错。
//返回值 jstring
//方法名:有讲究,首先是Java表明是java语言写的,然后为全类名,中间用下划线_连接,然后是(JNIEnv* env,jobject obj)参数
jstring Java_com_example_manzuo_JNIActivity_getMessageFromC(JNIEnv* env,jobject obj){
//通过查看jni.h文件定义我们知道 env 是个结构体的指针,那么*env 就是结构体实例,
//**env 就是JNINativeInterface ,结构体怎么引用 需要学习C语言。这里先不描述了。
// return **env.NewStringUTF(env,"message from C returned");
return (*env)->NewStringUTF(env,"message from C returned");
}</span>
1 :类型定义
/*
* Primitive types that match up with Java equivalents.
*/
#ifdef HAVE_INTTYPES_H
# include <inttypes.h> /* C99 */
typedef uint8_t jboolean; /* unsigned 8 bits */
typedef int8_t jbyte; /* signed 8 bits */
typedef uint16_t jchar; /* unsigned 16 bits */
typedef int16_t jshort; /* signed 16 bits */
typedef int32_t jint; /* signed 32 bits */
typedef int64_t jlong; /* signed 64 bits */
typedef float jfloat; /* 32-bit IEEE 754 */
typedef double jdouble; /* 64-bit IEEE 754 */
#else
typedef unsigned char jboolean; /* unsigned 8 bits */ 1个字节 占8位
typedef signed char jbyte; /* signed 8 bits */ 1个字节 占8位
typedef unsigned short jchar; /* unsigned 16 bits */ 2个字节 占16位
typedef short jshort; /* signed 16 bits */ 2个字节 占16位
typedef int jint; /* signed 32 bits */ 4个字节 占32位
typedef long long jlong; /* signed 64 bits */ 8个字节 占64位
typedef float jfloat; /* 32-bit IEEE 754 */ 4个字节 占32位
typedef double jdouble; /* 64-bit IEEE 754 */ 8个字节 占64位
#endif
通过以上定义可以看到java中的int 、float、double就是C 语言中的int 、float、double
2: 来看一下一个重要的结构体 类型:JNINativeInterface
typedef const struct JNINativeInterface* C_JNIEnv;
#if defined(__cplusplus) //C++
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else // C
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
#endif
这个结构体类型对应了一个java 虚拟机的实现,定义了很多java的方法。我这样理解,可以把这个结构体当做一个接口interface,
那么一个java虚拟机实现了这里面的方法,如果改了这个接口中的方法呢,java虚拟机中的实现也要改变。由于C语言中没对象一说,因此都需要一个专门的函数类返回一些 需要的类型数据。例如返回产生一个字符串就可以调用JNINativeInterface* 结构体类型中的方法:
jstring (*NewStringUTF)(JNIEnv*, const char*); //以UTF-8编码返回一个jstring类型(字符串)。
3:java中的object 就是C语言中的 一个不限长度的指针。 在jni.h文件中定义: typedef void* jobject;
4:。。。。还有很多其他的定义,可以慢慢熟悉。。。不会写了
三、把C代码 打包成函数库 :进入 当前c代码所在目录 ,执行ndk-build命令即可。
到这步时,如果直接编译 如下则报错。提示少Android.mk文件。
-bash-4.1$ cd jni/
-bash-4.1$ ls
Message.c
-bash-4.1$ ls -l
total 1
-rwx------+ 1 Administrators None 235 Sep 10 15:49 Message.c
-bash-4.1$ ndk-build
Android NDK: Your APP_BUILD_SCRIPT points to an unknown file: /cygdrive/e/workspace/testproject/jni/Android.mk
/cygdrive/d/setup/android-ndk-r7b/build/core/add-application.mk:133: *** Android NDK: Aborting... . Stop.
question:那么Android.mk文件是干什么的呢?
answer:这个文件用来 告诉编译器 如何把C代码 打包成函数库。
question:如何编写这个文件呢?
answer:我们可以在NDK安装目录下的 文档中找到来写,进入/NDK/docs/ANDOIRD-MK.html .观看文档复制下面内容即可。
<span style="font-size:14px;">---------- cut here ------------------
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# 对应的要生成的函数库文件的名字
LOCAL_MODULE := hello-jni
# 要编译的对应的C代码的文件
LOCAL_SRC_FILES := hello-jni.c
include $(BUILD_SHARED_LIBRARY)
---------- cut here ------------------</span>
到这里 新建Android.mk文件 ,写入上面配置项即可。再重新执行ndk-build命令编译 即可。如果编译成功则生成so文件。
(注意刷新工程可以查看)在Cygwin 中执行 ndk-build时 报错,打包不了,无奈用dos试试,居然成功了。
四、引入 so文件 并调用native方法即可。
package com.example.manzuo;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
public class JNIActivity extends Activity {
public native String getMessageFromC();
static {
// 注意引入时 用System.loadLibrary 方法。库的名字 为Android.mk文件中定义生成的库的名字
System.loadLibrary("Message");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.jni_page);
}
public void java_call_c(View v){
// 调用本地方法即可。
Toast.makeText(this, getMessageFromC(), 0).show();
}
}
----------------------------------------------------------------------------------------------C代码和Android.mk-------------------------------------------
/jni/Message.c :
#include <stdio.h>
#include <jni.h>
jstring Java_com_example_manzuo_JNIActivity_callC(JNIEnv* env,jobject obj){
// return **env.NewStringUTF(env,"message from C returned");
return (*env)->NewStringUTF(env,"message from C returned");
}
/jni/Android.mk :
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := Message
LOCAL_SRC_FILES := Message.c
include $(BUILD_SHARED_LIBRARY)
点击后效果: