android JNI学习二

在上一篇里记录了一个个人认为较简单的jni例子,接下来我写一下jni流程,个人观点,也是参考网上资料后写的一些观点吧,如有错误,可指正,谢谢。

以上一篇记录里的代码为举例,在java里

static {
System.load("/system/jni/libjnicall.so");
}

就是加载so库,这个是制定路径的加载,另外一种方法是System.loadLibrary,各位可自己百度差别,我就不细说了。当运行这里加载时,就会执行 JNI_OnLoad 函数(搜索到网上这样写)。这就是为什么一开始编译库文件跑起来会出错的原因。网上说即使没有这个 JNI_OnLoad 函数,也会返回最老的JNI版本(1.1版),但测试结果就是会运行报错,所以加上这个函数会返回1.4版(个人猜测)。通过返回jni版本,就能正确调用这个库文件了。定义jni里C函数:

jint Java_com_example_jnicall_MainActivity_add(JNIEnv* env, jobject this, jint add1, jint add2) 

返回的类型是jint,就是对应java里的int类型,函数名会比较长,因为要固定包名,前面Java的J一定要大写(网上说的),后面包名用下横杠替换 . ,由于我是直接在Activity里加载库和定义native接口,所以包名后跟MainActivity,最后就是native函数名了。这些都是以下横线隔开,具体原因可百度,好像说是系统对 . 有特殊用处,因此用下横线替换。参数前两个:JNIEnv* env, jobject this 是固定的可不理会,具体功能也可百度自己查。然后跟两个jint型参数add1和add2就是在java里所调用的方法:

add(1,2);

所传的1和2。

这次尝试把c文件改成cpp文件来编译,个人认为是可以使用C++语言来写jni了。

现在开始,首先是把jni_call.c文件名改为jni_call.cpp,tools.c文件名改为tools.cpp。修改Android.mk文件如下:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libtools
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := tools.cpp
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := libjnicall
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := jni_call.cpp
LOCAL_STATIC_LIBRARIES := libtools
LOCAL_MODULE_PATH := $(LOCAL_PATH)/out/lib
include $(BUILD_SHARED_LIBRARY)

其中,LOCAL_SRC_FILES := tools.cpp是把编译的文件名从tools.c改为tools.cpp。LOCAL_SRC_FILES := jni_call.cpp是从jni_call.c改为jni_call.cpp。

添加了一个LOCAL_MODULE_PATH := $(LOCAL_PATH)/out/lib ,是让编译出来的so库文件放到当前目录的out/lib/文件下,这样就不用进入system目录下,看个人习惯而已。

jni_call.cpp文件修改如下

#include "tools.h"
#include <jni.h>
#include "JNIHelp.h"

jint Java_com_example_jnicall_MainActivity_add(JNIEnv* env, jobject thiz, jint add1, jint add2){
    return add(add1, add2);
}

#ifdef __cplusplus
extern "C" {
#endif
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv *env;
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
return -1;
}
    return JNI_VERSION_1_4;
}
#ifdef __cplusplus
} // extern "C"
#endif

要把jint Java_com_example_jnicall_MainActivity_add(JNIEnv* env, jobject thiz, jint add1, jint add2) 当中的第二个参数 jobject this 改为 jobject thiz(可对照以前的代码)

因为在C++里面,this有特殊用处,所以以后看网上资料,可从这里做判断是使用C语言还是C++(个人观点)。

编译目录:mmm packages/apps/jni_test/ 

会报错:packages/apps/jni_test/jni_call.cpp:16:11: error: base operand of '->' has non-pointer type 'JavaVM {aka _JavaVM}'

这是因为改成了C++语言,传递的参数会有变化。

错误出现在这一句:

(*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK
各位可自己百度GetEnv的C和C++调用上是有区别的。

我个人认为,C语言使用 (*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4)

C++使用 vm->GetEnv((void**) &env, JNI_VERSION_1_4)

要注意,GetEnv的第一个参数也是不一样的,C++是没有第一个参数vm的。

还要用extern "C"把Java_com_example_jnicall_MainActivity_add函数也括进去。

jni_call.cpp文件修改如下:

#include "tools.h"
#include <jni.h>
#include "JNIHelp.h"

#ifdef __cplusplus
extern "C" {
#endif
jint Java_com_example_jnicall_MainActivity_add(JNIEnv* env, jobject thiz, jint add1, jint add2){
    return add(add1, add2);
}

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv *env;
if ( (vm->GetEnv( (void**) &env, JNI_VERSION_1_4)) != JNI_OK) {
return -1;
}
    return JNI_VERSION_1_4;
}
#ifdef __cplusplus
} // extern "C"
#endif

这时编译通过了:Install: packages/apps/jni_test/out/lib/libjnicall.so

libjnicall.so重新push进/system/jni/ 里面(如果涉及到权限问题,无法push进去,可以自己修改路径地址,只要在java的System.load方法里参数地址对应上就行)。

运行com.example.jnicall apk,可以正常运行,就表明用C++方法成功了。

尝试一下也可以如下修改:

#include "tools.h"
#include <jni.h>
#include "JNIHelp.h"
#ifdef __cplusplus
extern "C" {
#endif
jint Java_com_example_jnicall_MainActivity_add(JNIEnv* env, jobject thiz, jint add1, jint add2){
    return add(add1, add2);
}
#ifdef __cplusplus
} // extern "C"
#endif

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv *env;
if ( (vm->GetEnv( (void**) &env, JNI_VERSION_1_4)) != JNI_OK) {
return -1;
}
    return JNI_VERSION_1_4;
}

在函数 JNI_OnLoad 前加 JNIEXPORT 这个关键字,编译也能通过,而 Java_com_example_jnicall_MainActivity_add 函数仍然要用 extern "C" 括起来。

JNIEXPORT 的功能各位可自己百度,这里使用这个关键字是为了下次改用另外一种 jni 方法注册而准备的。


工程代码:http://download.csdn.net/detail/u013820413/6996101


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值