㈠ android下JNI C 调用java,可能是路径问题
JNI_CreateJavaVM 这个方法是不能直接在C中调用了。你看 jni.h 的头文件:
#if 0 /* In practice, these are not exported by the NDK so don't declare them */
jint JNI_GetDefaultJavaVMInitArgs(void*);
jint JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*);
jint JNI_GetCreatedJavaVMs(JavaVM**, jsize, jsize*);
#endif
这三个方法都是禁止调用的 ( if 0 )
㈡ android 怎么封装jni
一、底层实现:
c文件:hardware/libhardware_legacy/power/power.c
以其中set_screen_state(int)函数为例
其Android.mk中添加:
LOCAL_MODULE:= libpower 编译成lib
LOCAL_SRC_FILES += power.c
hardware/libhardware_legacy/power/power.c
1: int
2: set_screen_state(int on)
3: {
4: QEMU_FALLBACK(set_screen_state(on));
5:
6: LOGI("*** set_screen_state %d", on);
7:
8: initialize_fds();
9:
10: //LOGI("go_to_sleep eventTime=%lld now=%lld g_error=%s\n", eventTime,
11: // systemTime(), strerror(g_error));
12:
13: if (g_error)
14: goto failure;
15:
16: char buf[32];
17: int len;
18: if(on)
19: len = snprintf(buf, sizeof(buf), "%s", on_state);
20: else
21: len = snprintf(buf, sizeof(buf), "%s", off_state);
22:
23: buf[sizeof(buf) - 1] = '\0';
24: len = write(g_fds[REQUEST_STATE], buf, len);
25: if(len < 0) {
26: failure:
27: LOGE("Failed setting last user activity: g_error=%d\n", g_error);
28: }
29: return 0;
30: }
其头文件power.h中:
1: #if__cplusplus
2: extern "C" { //注1
3: #endif
4: enum {
5: PARTIAL_WAKE_LOCK = 1, // the cpu stays on, but the screen is off
6: FULL_WAKE_LOCK = 2 // the screen is also on
7: };
8:
9: // while you have a lock held, the device will stay on at least at the
10: // level you request.
11: int acquire_wake_lock(int lock, const char* id);
12: int release_wake_lock(const char* id);
13:
14: // true if you want the screen on, false if you want it off
15: int set_screen_state(int on);
16:
17: // set how long to stay awake after the last user activity in seconds
18: int set_last_user_activity_timeout(int64_t delay);
19:
20:
21: #if __cplusplus
22: } // extern "C"
23: #endif
注1:
注1:extern表示其他的类已经定义了这段代码里面的内容,这里只是做声明。
"C”表示的一种编译和连接规约,这里为下一步c++调用其做准备.
比如void foo(int,int);该函数被C编译器编译后在库中的名字为_foo,
而C++编译器则会产生像_foo_int_int之类的名字用来支持函数重载和类型安全连接。
由于编译后的名字不同,C++程序不能直接调用C函数。
因此C++提供了一个C连接交换指定符号extern“C”来解决这个问题而不是一种语言。
C表示这段代码可以是符合C语言的编译和连接规约的任何语言,如Fortran、assembler等。
二、cpp构成jni桥梁
一个CPP文件调用之,则需添加其头文件,比如frameworks/base/core/jni/android_os_Power.cpp.
1: #include "JNIHelp.h"
2: #include "jni.h"
3: #include "android_runtime/AndroidRuntime.h"
4: #include <hardware_legacy/power.h>
5: namespace android{
6: ....
7:
8: //定义函数:
9: static int setScreenState(JNIEnv *env, jobject clazz, jboolean on)
10: {
11: return set_screen_state(on);//以此实现cpp到c的调用
12: }
13:
14: static JNINativeMethod method_table[] = {//此处实现java对cpp的调用转化 注2
15: { "acquireWakeLock", "(ILjava/lang/String;)V", (void*)acquireWakeLock },
16: { "releaseWakeLock", "(Ljava/lang/String;)V", (void*)releaseWakeLock },
17: { "setLastUserActivityTimeout", "(J)I", (void*)setLastUserActivityTimeout },
18: { "setScreenState", "(Z)I", (void*)setScreenState },
19: { "shutdown", "()V", (void*)android_os_Power_shutdown },
20: { "rebootNative", "(Ljava/lang/String;)V", (void*)android_os_Power_reboot },
21: };
22: int register_android_os_Power(JNIEnv *env) //此处注册jni
23: { //向VM(即AndroidRuntime)登记 gMethods[]表格所含的本地函数
24: return AndroidRuntime::registerNativeMethods(
25: env, "android/os/Power",
26: method_table, NELEM(method_table));
27: }
28: };
注2:
typedef struct {
const char* name; //Java中函数的名字
const char* signature; //用字符串是描述了函数的参数和返回值
void* fnPtr; //函数指针,指向C函数
} JNINativeMethod;
其中比较难以理解的是第二个参数,例如
"()V"
"(II)V"
"(Ljava/lang/String;Ljava/lang/String;)V"
实际上这些字符是与函数的参数类型一一对应的。
"()" 中的字符表示参数,后面的则代表返回值。例如"()V" 就表示void Func();
"(II)V" 表示 void Func(int, int);
具体的每一个字符的对应关系如下
字符 Java类型 C类型
V void void
Z jboolean boolean
I jint int
J jlong long
D jdouble double
F jfloat float
B jbyte byte
C jchar char
S jshort short
数组则以"["开始,用两个字符表示
[I jintArray int[]
[F jfloatArray float[]
[B jbyteArray byte[]
[C jcharArray char[]
[S jshortArray short[]
[D jdoubleArray double[]
[J jlongArray long[]
[Z jbooleanArray boolean[]
上面的都是基本类型。如果Java函数的参数是class,则以"L"开头,以";"结尾中间是用"/" 隔开的包及类名。而其对应的C函数名的参数则为jobject. 一个例外是String类,其对应的类为jstring
Ljava/lang/String; String jstring
Ljava/net/Socket; Socket jobject
如果JAVA函数位于一个嵌入类,则用$作为类名间的分隔符。
例如 "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z"
三、java的封装实现
frameworks/base/core/java/android/os/Power.java //此处路径跟cpp中注册jni处的路径是一致的.待细研究是否有关系
1: package android.os;
2: public class Power
3: {
4: ...
5: public static native int setScreenState(boolean on); //被native修饰的表示调用了非java语言的本地方法
6: ...
7: }
四、java中对其调用
frameworks/base/services/java/com/android/server/PowerManagerService.java
import android.os.Power;
public class PowerManagerService extends IPowerManager.Stub
implements LocalPowerManager, Watchdog.Monitor {
...
int err = Power.setScreenState(on);
...
}
㈢ android studio 2.0 include lt;jni.h>找不到 有知道的小伙伴吗
总共有三种方式生成Jni头文件,但归根结底只有一种,只是操作方法不一样,我在这里做一下记录,方便下次使用。1.11.利用AndroidStudio的Terminal,进入你自己的Android工程文件的app/src/main目录,例如(D:\Users\AndroidStudio\Application\app\src\main)在Terminal中输入命令cdapp\src\main这样就进入到了main目录,可以使用dir命令查看当前目录有哪些文件总共有三种方式生成Jni头文件2.在Terminal中输入命令javah-djni-jni-classpath..\..\build\intermediates\classes\debugcom.example.lenovo.application.JniClass解释:-d输出目录,后面跟上要生成的目录名-jni生成Jni样式的标头文件-classpath指定加载类的路径,后面跟上你要生成头文件的这个类的路径,例如:..\..\..\build\intermediates\classes\debug(这个是类所在的路径)com.example.lenovo.application.JniClass(类的包名)注意,要生成.class文件,要MakeProject3.如果前面没有出错的话,就能看到jni文件和生成的头文件其实jni目录可以不用命令生成,AndroidStudio已经为我们准备好了。这样就能快速的生成jni目录了。然后在Terminal中进入到该目录,输入命令cdapp\src\main\jni在执行如下命令javah-djni-jni-classpath..\..\build\intermediates\classes\debugcom.example.lenovo.application.JniClass这样也能生成头文件利用Windows的cmd生成jni头文件1.进入到你自己Android工程目录下的app\build\intermediates\classes\debug2.然后Shift加鼠标右键,点击红色方框的内容3.出现cmd命令行,输入如下命令javah-jnicom.example.lenovo.application.JniClass4.会在当前目录生成*.h头文件,把它拷贝进jni目录就可以了。jni目录可以按照上面的方法创建至此,生成jni头文件的三种方法讲解完毕。
㈣ 如何在android源码中添加自己的jni方法
1,、项目实现了一个简单的四则运算,项目的目录层次如下:
AndroidManifest.xml Android.mk jni res src
资源文件简简单单,一个布局文件,稍后会有demo的下载地址
主要记录备忘的内容如下:
MainActivity.Java
[html] view plain public native int add(int x, int y);
public native int substraction(int x, int y);
public native float multiplication(int x, int y);
public native float division(int x, int y);
static{
System.loadLibrary("arithmetic");
}
2、生成lib的名称为libarithmetic.so.注意load的时候写"arithmetic"
jni 目录下有两个文件,一个是Android.mk,一个是c++源文件long.cpp
jni/Android.mk如下:
[html] view plain LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE:= libarithmetic
LOCAL_SRC_FILES:= \
long.cpp
LOCAL_SHARED_LIBRARIES := \
libutils
LOCAL_STATIC_LIBRARIES :=
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE)
LOCAL_CFLAGS +=
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
3、注释:
LOCAL_PATH(必须定义,而且要第一个定义),宏函数‘my-dir’, 由编译系统提供,用于返回当前路径(即包含Android.mk
file文件的目录);
include $( CLEAR_VARS),
CLEAR_VARS由编译系统提供,指定让GNU MAKEFILE为你清除许多LOCAL_XXX变量(例如 LOCAL_MODULE,
LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, 等等...),除LOCAL_PATH
。这是必要的,因为所有的编译控制文件都在同一个GNU MAKE执行环境中,所有的变量都是全局的;
LOCAL_MODULE_TAGS :=user eng tests optional
user: 指该模块只在user版本下才编译
eng: 指该模块只在eng版本下才编译
tests: 指该模块只在tests版本下才编译
optional:指该模块在所有版本下都编译
LOCAL_MODULE(必须定义),标识你在Android.mk文件中描述的每个模块。名称必须是唯一的,而且不包含任何空格。Note:编译系统会自动产生合适的前缀和后缀,例如:arithmetic编译成功后将生成libarithmetic.so库文件
LOCAL_SRC_FILES 变量必须包含将要编译打包进模块中源代码文件。不用在这里列出头文件和包含文件。
LOCAL_SHARED_LIBRARIES中加入所需要链接的动态库(*.so)的名称
LOCAL_STATIC_LIBRARIES加入所需要链接的静态库(*.a)的名称
LOCAL_CFLAG可选的编译器选项,用法之一是定义宏,例如LOCAL_CFLAGS := -Werror作用是编译警告也作为错误信息
LOCAL_PRELINK_MODULE:=false,不作prelink处理,默认是要prelink操作的,有可能造成地址空间冲突(这地方目前还不明白)
long.cpp源代码如下:
[html] view plain #define LOG_TAG "LongTest2 long.cpp"
#include
#include
#include "jni.h"
jint add(JNIEnv *env, jobject thiz, jint x, jint y){
return x + y;
}
jint substraction(JNIEnv *env, jobject thiz, jint x, jint y){
return x - y;
}
jfloat multiplication(JNIEnv *env, jobject thiz, jint x, jint y){
return (float)x * (float)y;
}
jfloat division(JNIEnv *env, jobject thiz, jint x, jint y){
return (float)x/(float)y;
}
static const char *classPathName = "com/inspur/test2/MainActivity";
static JNINativeMethod methods[]= {
{"add", "(II)I", (void*)add},
{"substraction", "(II)I", (void*)substraction},
{"multiplication", "(II)F", (void*)multiplication},
{"division", "(II)F", (void*)division},
};
typedef union{
JNIEnv* env;
void* venv;
}UnionJNIEnvToVoid;
static int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* gMethods, int numMethods){
jclass clazz;
clazz = env->FindClass(className);
if (clazz == NULL)
return JNI_FALSE;
if (env->RegisterNatives(clazz, gMethods, numMethods)<0)
return JNI_FALSE;
return JNI_TRUE;
}
static int registerNatives(JNIEnv *env){
if (!registerNativeMethods(env, classPathName,
methods, sizeof(methods)/sizeof(methods[0])))
{
return JNI_FALSE;
}
return JNI_TRUE;
}
jint JNI_OnLoad(JavaVM* vm, void* reserved){
UnionJNIEnvToVoid uenv;
uenv.venv = NULL;
jint result = -1;
JNIEnv *env = NULL;
if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK){
goto l;
}
env = uenv.env;
env = uenv.env;
if (registerNatives(env) != JNI_TRUE){
goto l;
}
result = JNI_VERSION_1_4;
l:
return result;
}
除了利用 编写native JAVA类,通过javah生成.h文件,根据.h文件编写.c/cpp文件
方法外(名字像老太太的裹脚步,又臭又长,而且不灵活),Android还可以通过引用JNI_Onload方式实现。jint JNI_onLoad(JavaVM*
vm, void* reverced),改方法在so文件被加载时调用。
JNI_OnLoad()有两个重要的作用:
指定JNI版本:告诉VM该组件使用那一个JNI版本(若未提供JNI_OnLoad()函数,VM会默认该使用最老的JNI
1.1版),如果要使用新版本的JNI,例如JNI
1.4版,则必须由JNI_OnLoad()函数返回常量JNI_VERSION_1_4(该常量定义在jni.h中) 来告知VM。
初始化设定,当VM执行到System.loadLibrary()函数时,会立即先呼叫JNI_OnLoad()方法,因此在该方法中进行各种资源的初始化操作最为恰当。
JNI_OnUnload()的作用与JNI_OnLoad()对应,当VM释放JNI组件时会呼叫它,因此在该方法中进行善后清理,资源释放的动作最为合适。
4、项目根目录下Android.mk文件:
[html] view plain LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_JNI_SHARED_LIBRARIES := libarithmetic
LOCAL_PACKAGE_NAME := LongTest
LOCAL_SHARED_LIBRARIES := \
libutils\
liblog
include $(BUILD_PACKAGE)
include $(LOCAL_PATH)/jni/Android.mk
# Also build all of the sub-targets under this one: the shared library.
include $(call all-makefiles-under,$(LOCAL_PATH))
LOCAL_PACKAGE_NAME:项目名称,即最终生成apk的名字
LOCAL_JNI_SHARED_LIBRARIES := libxxx就是把so文件放到apk文件里的libs/armeabi里
执行BUILD_PACKAGE。它的定义也是在config.mk中定义如下:BUILD_PACKAGE:=
$(BUILD_SYSTEM)/package.mk
$(call all-java-files-under, src)编译的源代码文件列表添加src目录下所有的java 源文件
$(call all-makefiles-under, $(LOCAL_PATH))编译器会在编译完当前目录下的文件后再深入子目录编译
如果make过android源码,可以在项目根目录下执行mm命令进行编译。前提是执行过source
androidSRC/build/envsetup.sh
或者直接把source androidSRC/build/envsetup.sh添加到~/.bashrc中,会更加方便
㈤ android studio 开发 jni目录默认放哪个文件夹
1 在交叉编译的时候怎么都无法生成so文件,javah生成头文件没错,c文件也没错,java文件也没错,
2.原因:是JNI文件夹路径不对
3 在执行javah命令时,我进入的是cd app/src/main/java 这样jni文件夹在java文件夹下,作为一个包存在,这样就无法生成so文件
执行javah的正确姿势:
4 进入app/src/main目录:cd app/src/main
执行javah命令:javah
javah -d jni -classpath ./Java lab.sodino.jnitest.MainActivity
5, -d jni 头文件生成到jni文件夹(当前在<Project>\app\src\main目录下,所以.h所在的目录为<Project>\app\src\main\jni )
-classpath ./java 指定去当前路径下java下寻找包名指定的类
这样再rebuild一下,就会生成so文件了
㈥ 如何在Android下使用JNI
1.引言
我们知道,Android系统的底层库由c/c++编写,上层Android应用程序通过Java虚拟机调用底层接口,衔接底层c/c++库与Java应用程序间的接口正是JNI(JavaNative Interface)。本文描述了如何在ubuntu下配置AndroidJNI的开发环境,以及如何编写一个简单的c函数库和JNI接口,并通过编写Java程序调用这些接口,最终运行在模拟器上的过程。
2.环境配置
2.1.安装jdk1.6
(1)从jdk官方网站下载jdk-6u29-linux-i586.bin文件。
(2)执行jdk安装文件
[html] view plainprint?
01.$chmod a+x jdk-6u29-linux-i586.bin
02.$jdk-6u29-linux-i586.bin
$chmod a+x jdk-6u29-linux-i586.bin
$jdk-6u29-linux-i586.bin
(3)配置jdk环境变量
[html] view plainprint?
01.$sudo vim /etc/profile
02.#JAVAEVIRENMENT
03.exportJAVA_HOME=/usr/lib/java/jdk1.6.0_29
04.exportJRE_HOME=$JAVA_HOME/jre
05.exportCLASSPATH=$JAVA_HOME/lib:$JRE_HOME/lib:$CLASSPATH
06.exportPATH=$JAVA_HOME/bin:$JRE_HOME/bin:$PATH
$sudo vim /etc/profile
#JAVAEVIRENMENT
exportJAVA_HOME=/usr/lib/java/jdk1.6.0_29
exportJRE_HOME=$JAVA_HOME/jre
exportCLASSPATH=$JAVA_HOME/lib:$JRE_HOME/lib:$CLASSPATH
exportPATH=$JAVA_HOME/bin:$JRE_HOME/bin:$PATH
保存后退出编辑,并重启系统。
(4)验证安装
[html] view plainprint?
01.$java -version
02.javaversion "1.6.0_29"
03.Java(TM)SE Runtime Environment (build 1.6.0_29-b11)
04.JavaHotSpot(TM) Server VM (build 20.4-b02, mixed mode)
05.$javah
06.用法:javah[选项]<类>
07.其中[选项]包括:
08.-help输出此帮助消息并退出
09.-classpath<路径>用于装入类的路径
10.-bootclasspath<路径>用于装入引导类的路径
11.-d<目录>输出目录
12.-o<文件>输出文件(只能使用-d或-o中的一个)
13.-jni生成JNI样式的头文件(默认)
14.-version输出版本信息
15.-verbose启用详细输出
16.-force始终写入输出文件
17.使用全限定名称指定<类>(例
18.如,java.lang.Object)。
$java -version
javaversion "1.6.0_29"
Java(TM)SE Runtime Environment (build 1.6.0_29-b11)
JavaHotSpot(TM) Server VM (build 20.4-b02, mixed mode)
$javah
用法:javah[选项]<类>
其中[选项]包括:
-help输出此帮助消息并退出
-classpath<路径>用于装入类的路径
-bootclasspath<路径>用于装入引导类的路径
-d<目录>输出目录
-o<文件>输出文件(只能使用-d或-o中的一个)
-jni生成JNI样式的头文件(默认)
-version输出版本信息
-verbose启用详细输出
-force始终写入输出文件
使用全限定名称指定<类>(例
如,java.lang.Object)。2.2.安装android应用程序开发环境
ubuntu下安装android应用程序开发环境与windows类似,依次安装好以下软件即可:
(1)Eclipse
(2)ADT
(3)AndroidSDK
与windows下安装唯一不同的一点是,下载这些软件的时候要下载Linux版本的安装包。
安装好以上android应用程序的开发环境后,还可以选择是否需要配置emulator和adb工具的环境变量,以方便在进行JNI开发的时候使用。配置步骤如下:
把emulator所在目录android-sdk-linux/tools以及adb所在目录android-sdk-linux/platform-tools添加到环境变量中,android-sdk-linux指androidsdk安装包android-sdk_rxx-linux的解压目录。
[plain] view plainprint?
01.$sudo vim /etc/profile
02.exportPATH=~/software/android/android-sdk-linux/tools:$PATH
03. exportPATH=~/software/android/android-sdk-linux/platform-tools:$PATH
$sudo vim /etc/profile
exportPATH=~/software/android/android-sdk-linux/tools:$PATH
exportPATH=~/software/android/android-sdk-linux/platform-tools:$PATH
编辑完毕后退出,并重启生效。
2.3.安装NDK
NDK是由android提供的编译android本地代码的一个工具。
(1)从androidndk官网http://developer.android.com/sdk/ndk/index.html下载ndk,目前最新版本为android-ndk-r6b-linux-x86.tar.bz2.
(2)解压ndk到工作目录:
[plain] view plainprint?
01.$tar -xvf android-ndk-r6b-linux-x86.tar.bz2
02.$sudo mv android-ndk-r6b /usr/local/ndk
$tar -xvf android-ndk-r6b-linux-x86.tar.bz2
$sudo mv android-ndk-r6b /usr/local/ndk
(3)设置ndk环境变量
[plain] view plainprint?
01.$sudo vim /etc/profile
02.exportPATH=/usr/local/ndk:$PATH
$sudo vim /etc/profile
exportPATH=/usr/local/ndk:$PATH
编辑完毕后保存退出,并重启生效
(4)验证安装
[plain] view plainprint?
01.$ cd/usr/local/ndk/samples/hello-jni/
02.$ ndk-build
03.Gdbserver : [arm-linux-androideabi-4.4.3] libs/armeabi/gdbserver
04.Gdbsetup : libs/armeabi/gdb.setup
05.Install : libhello-jni.so => libs/armeabi/libhello-jni.so
$ cd/usr/local/ndk/samples/hello-jni/
$ ndk-build
Gdbserver : [arm-linux-androideabi-4.4.3] libs/armeabi/gdbserver
Gdbsetup : libs/armeabi/gdb.setup
Install : libhello-jni.so => libs/armeabi/libhello-jni.so
3.JNI实现
我们需要定义一个符合JNI接口规范的c/c++接口,这个接口不用太复杂,例如输出一个字符串。接下来,则需要把c/c++接口的代码文件编译成共享库(动态库).so文件,并放到模拟器的相关目录下。最后,启动Java应用程序,就可以看到最终效果了。
3.1.编写Java应用程序代码
(1)启动Eclipse,新建android工程
Project:JNITest
Package:org.tonny.jni
Activity:JNITest
(2)编辑资源文件
编辑res/values/strings.xml文件如下:编辑res/layout/main.xml文件
我们在主界面上添加了一个EditText控件和一个Button控件。
(3)编辑JNITest.java文件
static表示在系统第一次加载类的时候,先执行这一段代码,在这里表示加载动态库libJNITest.so文件。
再看这一段:
[java] view plainprint?
01.privatenativeString GetReply();
privatenativeString GetReply();
native表示这个方法由本地代码定义,需要通过jni接口调用本地c/c++代码。
[java] view plainprint?
01.publicvoidonClick(View arg0) {
02.edtName.setText(reply);
03.}
publicvoidonClick(View arg0) {
edtName.setText(reply);
}
这段代码表示点击按钮后,把native方法的返回的字符串显示到EditText控件。
(4)编译工程,生成.class文件。
3.2.用javah工具生成符合JNI规范的c语言头文件
在终端中,进入android工程所在的bin目录
[plain] view plainprint?
01.$cd ~/project/Android/JNITest/bin
$cd ~/project/Android/JNITest/bin
我们用ls命令查看,可以看到bin目录下有个classes目录,其目录结构为classes/org/tonny/jni,即classes的子目录结构是android工程的包名org.tonny.jni。请注意,下面我们准备执行javah命令的时候,必须进入到org/tonny/jni的上级目录,即classes目录,否则javah会提示找不到相关的java类。
下面继续:
[plain] view plainprint?
01.$cd classes
02.$javah org.tonny.jni.JNITest
03.$ls
04.org org_tonny_jni_JNITest.h
$cd classes
$javah org.tonny.jni.JNITest
$ls
org org_tonny_jni_JNITest.h
执行javahorg.tonny.jni.JNITest命令,在classes目录下会生成org_tonny_jni_JNITest.h头文件。如果不进入到classes目录下的话,也可以这样:
[plain] view plainprint?
01.$javah -classpath ~/project/Android/JNITest/bin/classesorg.tonny.jni.JNITest
$javah -classpath ~/project/Android/JNITest/bin/classesorg.tonny.jni.JNITest
-classpath 参数表示装载类的目录。
3.3.编写c/c++代码
生成org_tonny_jni_JNITest.h头文件后,我们就可以编写相应的函数代码了。下面在android工程目录下新建jni目录,即~/project/Android/JNITest/jni,把org_tonny_jni_JNITest.h头文件拷贝到jni目录下,并在jni目录下新建org_tonny_jni_JNITest.c文件,编辑代码如下:
[cpp] view plainprint?
01.#include<jni.h>
02.#include<string.h>
03.#include"org_tonny_jni_JNITest.h"
04.
05.
06.JNIEXPORTjstring JNICALLJava_org_tonny_jni_JNITest_GetReply
07.(JNIEnv *env, jobject obj){
08.return(*env)->NewStringUTF(env,(char*)"Hello,JNITest");
09.}
#include<jni.h>
#include<string.h>
#include"org_tonny_jni_JNITest.h"
JNIEXPORTjstring JNICALLJava_org_tonny_jni_JNITest_GetReply
(JNIEnv *env, jobject obj){
return(*env)->NewStringUTF(env,(char*)"Hello,JNITest");
}
我们可以看到,该函数的实现相当简单,返回一个字符串为:"Hello,JNITest"
3.4.编写Android.mk文件
在~/project/Android/JNITest/jni目录下新建Android.mk文件,android可以根据这个文件的编译参数编译模块。编辑Android.mk文件如下:
[plain] view plainprint?
01.LOCAL_PATH:= $(call my-dir)
02.include$(CLEAR_VARS)
03.LOCAL_MODULE := libJNITest
04.LOCAL_SRC_FILES:= org_tonny_jni_JNITest.c
05.include$(BUILD_SHARED_LIBRARY)
LOCAL_PATH:= $(call my-dir)
include$(CLEAR_VARS)
LOCAL_MODULE := libJNITest
LOCAL_SRC_FILES:= org_tonny_jni_JNITest.c
include$(BUILD_SHARED_LIBRARY)
LOCAL_MODULE表示编译的动态库名称
LOCAL_SRC_FILES 表示源代码文件
3.5.用ndk工具编译并生成.so文件
进入到JNITest的工程目录,执行ndk-build命令即可生成libJNITest.so文件。
[plain] view plainprint?
01.$cd ~/project/Android/JNITest/
02.$ndk-build
03.Invalidattribute name:
04.package
05.Install : libJNITest.so => libs/armeabi/libJNITest.so
$cd ~/project/Android/JNITest/
$ndk-build
Invalidattribute name:
package
Install : libJNITest.so => libs/armeabi/libJNITest.so
可以看到,在工程目录的libs/armeabi目录下生成了libJNITest.so文件。
3.6.在模拟器上运行
(1)首先,我们把android模拟器启动起来。进入到emulator所在目录,执行emulator命令:
[plain] view plainprint?
01.$cd ~/software/android/android-sdk-linux/tools
02.$./emulator @AVD-2.3.3-V10 -partition-size 512
$cd ~/software/android/android-sdk-linux/tools
$./emulator @AVD-2.3.3-V10 -partition-size 512
AVD-2.3.3-V10表示你的模拟器名称,与在Eclipse->AVDManager下的AVDName对应,-partition-size表示模拟器的存储设备容量。
(2)接下来,我们需要把libJNITest.so文件拷贝到模拟器的/system/lib目录下,执行以下命令:
[plain] view plainprint?
01.$cd ~/project/Android/JNITest/libs/armeabi/
02.$adb remount
03.$adb push libJNITest.so /system/lib
04.80 KB/s (10084 bytes in 0.121s)
$cd ~/project/Android/JNITest/libs/armeabi/
$adb remount
$adb push libJNITest.so /system/lib
80 KB/s (10084 bytes in 0.121s)
当在终端上看到有80 KB/s (10084 bytes in 0.121s)传输速度等信息的时候,说明拷贝成功。
(3)在终端上执行JNITest程序,这个我们可以在Eclipse下,右键点击JNITest工程,RunAs->Android Application,即可在模拟器上启动程序