JNI(二)、 Java 代码调用 C代码


环境搭建好之后,来写写第一小例子,调用一个无参的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>


jni.h头文件 说明理解:这是一个重要的头文件,里面声明了 和java类型对应的C语言的类型,还声明了一些函数等。

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)

点击后效果:





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值