JNI起的是一个桥梁的作用,可以转化java与c/c++的一些区别,比如数据类型等。之后我们将转换好的c代码生成动态链接库,供java调用。
下面以一个示例做说明,希望对刚接触的童鞋有所帮助。
1、安装NDK环境:这个可以参照我的前一篇文章“windows系统上安装与使用Android NDK r5”。这是一个童鞋写的,照着做就可以完全没有问题的安装好。
2、当你按照步骤1安装好环境后,比如安装在了D盘D:\android-ndk-r6,本文以下默认此路径,如果你的路径不同 做相应改变就好了。然后在D:\android-ndk-r6下新建apps文件夹,里面存放的就是你将来要搞的android jni 工程相关文件啦。
现在我在D:\android-ndk-r6\apps 下创建我的第一个工程目录Angry,注意这个Angry并不是你的android工程的根目录,在Angry下创建project文件,这下面存放的将是你的android工程。 以下是我的目录结构:
之所以这样是因为,大家可以看到在Angry下还有个Application.mk文件(下面讲这个文件的生成),这个文件里是我们将c/c++编译成动态链接库(.so)的时候需要使用到的配置信息。除这个文件外我们还会用到一个Android.mk文件,在下面会讲到。
3.好了,接下来就创建我们的android工程吧。创建Android工程,指定工程路径:D:\android-ndk-r6\apps\Angry\project,以下是我创建的工程:
创建好了之后就可以写我们的java接口了(相当于函数声明),真正的功能实现是要用c/c++实现的吆。你可以新建一个java文件,或者直接在你继承Activity的那个java中直接写,我是新建了一个Jni.java文件:
Jni.java中的代码如下:
- <span style="font-family:Comic Sans MS;">package com.Angry;
- public class Jni {
- public native String getString ();
- }
- </span>
注意接口要声明成native的吆。如果你不想单独放在一个文件中也可以直接放在MainAct.java中,将 public native String getString (); 放进去就好了。
4.生成头文件以及源文件:
将Jni.java复制到D:\android-ndk-r6\apps\Angry\project\bin下面,然后在D:\android-ndk-r6\apps\Angry\project\bin用“javac jni.java”编译生成Jni.class文件,将生成的Jni.class复制到 D:\android-ndk-r6\apps\Angry\project\bin\com\Angry下覆盖掉原先的Jni.class文件。
然后再在D:\android-ndk-r6\apps\Angry\project\bin(建个目录文件com/Angry目录,把class文件考进去)下使用命令 "javah -jni com.Angry.Jni" 这是将会在bin下生成一个com_Angry_Jni.h的头文件,大家可以看一下我的生成:、
- <span style="font-family:Comic Sans MS;color:#009900;">/* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- /* Header for class com_Angry_Jni */
- #ifndef _Included_com_Angry_Jni
- #define _Included_com_Angry_Jni
- #ifdef __cplusplus
- extern "C" {
- #endif
- /*
- * Class: com_Angry_Jni
- * Method: getString
- * Signature: ()Ljava/lang/String;
- */
- JNIEXPORT jstring JNICALL Java_com_Angry_Jni_getString
- (JNIEnv *, jobject);
- #ifdef __cplusplus
- }
- #endif
- #endif</span>
其实就是将我们原先Jni.java中的public native String getString ();函数声明形式做了些许改动,使之符合JNI规则。
之后我们要在D:\android-ndk-r6\apps\Angry\project下即Android工程下新建jni文件夹,将com_Angry_Jni.h文件放入其中。这个jni文件夹将是盛放我们c/c++源文件以及头文件和Android.mk文件的场所。
接下来就是实现我们的函数功能了:新建c/c++源文件:com_Angry_Jni.c(同样放在jni文件夹下):
- #include <stdio.h>
- #include <stdlib.h>
- #include "com_Angry_Jni.h"
- JNIEXPORT jstring JNICALL Java_com_Angry_Jni_getString
- (JNIEnv *env, jobject thiz) {
- return (*env)->NewStringUTF(env, "I,M ANGRY");
- }
像我就只是单纯的实现了返回一字符串的功能,具体功能大家可以自己添加。程序中的jstring 等数据类型其实jni对c/c++的数据类型做了形式的改变,jstring 就是String。
现在功能都实现好了,我们可以将上面的c代码编译成so文件供java调用(因为android是基于linux的,而linux的动态链接库文件是以.so为后缀,windows以.dll为后缀)。
5、编译生成.so文件需要两个.mk文件,就是我们上面提到的Application.mk文件和Android.mk,在D:\android-ndk-r6\apps\Angry\project\jni下创建Android.mk,内容如下:
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_MODULE := Angry
- LOCAL_SRC_FILES := com_Angry_Jni.c
- include $(BUILD_SHARED_LIBRARY)
LOCAL_PATH: 表示位于工程的根目录。
include $(CLEAR_VARS) 功能还不太清楚我。
LOCAL_MODULE := Angry 这个表明你的一个动态链接库的名字,所以你的链接库生成后会叫libAngry.so,libxxx.so是系统加的,xxx即此处的名字。
LOCAL_SRC_FILES := com_Angry_Jni.c 这是你要编译的源文件。
还有好多配置指令,有兴趣大家可以自己查。
然后再在D:\android-ndk-r6\apps\Angry 下创建Applicetion.mk文件:
- APP_PROJECT_PATH := $(call my-dir)/project
- APP_MODULES := Angry
APP_MODULES := Angry 指明了你的应用模块。
6、到此为止,我们都把准备工作做好了,接下来生成.so文件:
启动cygwin,进入NDK根目录:D:\android-ndk-r6,运行命令:make APP=Angry 如果显示:
则表示.so生成并安装成功,你可以看到在 D:\android-ndk-r6\apps\Angry\project 下多了一个两层目录libs\armeabi\ 下面放着我们生成的libAngry.so。
7、到此为止,基本大功告成,下面我们验证一下c函数的功能能否实现:
在MainAct.java中实现如下简单代码:
- package com.Angry;
- import android.app.Activity;
- import android.os.Bundle;
- import android.widget.TextView;
- public class MainAct extends Activity {
- private TextView tv;
- /** Called when the activity is first created. */
- static
- {
- System.loadLibrary("Angry");
- }
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- Jni jni = new Jni();
- tv = (TextView)findViewById(R.id.tv);
- tv.setText(jni.getString());
- }
- }
运行下,看看效果: