JAVA+jni 包名或方法名中含有下划线(_)的解决方法

jni或NDK定义C函数是用下划线(_)作为命名分隔。但如果JAVA包名或方法名里包含了_ 那么就会发生找不到函数的错误,对此网上的各教程都说不要使用 _,对于新项目,这种情况当然很容易避免,但若对一原有项目开发jni接口就麻烦了,特别是在 Android 平台下,包名决定着应用的唯一识别,轻易改不的。

经过不懈的努力,我终于找到了解决方法,简单来说就是在 _ 后加1,如:

// JAVA
package net.zhigang.ndk_test;

public class NdkTest
{
    static {
        System.loadLibrary(ndktest);
    }
    public native void sayHello();
}

//C
#include <jni.h>

jstring Java_net_zhigang_ndk_1test(JNIEnv* env, jobject thiz)
{
    return env->NewStringUTF("Hello from jni");
}

注意上面C语言部分的 ndk_1test。

移植libcurl到Android NDK

测试通过环境:
操作系统:Mac OS
NDK:NDK r8e
libcurl:7.31.0

下载NDK环境: http://developer.android.com/tools/sdk/ndk/index.html ,安装环境不废话了。

在NDK安装目录执行

./build/tools/make-standalone-toolchain.sh

导出编译工具,一般生成压缩如 /tmp/ndk-zhigang/arm-linux-androideabi-4.6.tar.bz2。

解压:

cd /opt/
sudo tar xf /tmp/ndk-zhigang/arm-linux-androideabi-4.6.tar.bz2

下载curl源代码:http://curl.haxx.se/download.html

设置编译环境:

export LDFLAGS="\
-L/usr/local/Cellar/android-ndk/r8e/platforms/android-14/arch-arm/usr/lib"

export CPPFLAGS="\
-I/usr/local/Cellar/android-ndk/r8e/platforms/android-14/arch-arm/usr/include"

将以上路径修改为自己的NDK安装目录。

进入curl目录:

./configure --host=arm-linux-androideabi \
--disable-ftp \
--disable-gopher \
--disable-file \
--disable-imap \
--disable-ldap \
--disable-ldaps \
--disable-pop3 \
--disable-proxy \
--disable-rtsp \
--disable-smtp \
--disable-telnet \
--disable-tftp \
--without-gnutls \
--without-libidn \
--without-librtmp \
--without-ssl \
--disable-dict

mark

生成的静态库文件位于:lib/.libs/libcurl.a 动态库:libcurl.so.5.3.0

一般我们使用静态库文件。

复制 libcurl.a 到项目 jni/ 目录,修改 Android.mk 文件:

# A simple test for the minimal standard C++ library
#

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := curl
LOCAL_SRC_FILES := libcurl.a
include $(PREBUILT_STATIC_LIBRARY)

include $(CLEAR_VARS)
LOCAL_CFLAGS = -Wno-psabi
LOCAL_MODULE := curltest
LOCAL_SRC_FILES := curltest.c
LOCAL_STATIC_LIBRARIES := libcurl
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
LOCAL_LDLIBS    += -lz

参考:http://stackoverflow.com/questions/11330180/porting-libcurl-on-android-with-ssl-support

NDK(jni) JAVA与C互传数组对象

最近在搞NDK底层一些功能的开发,被一个问题折腾了3天,起初一直以为是C这边的位运算出了问题,查到最后才发现是数组传递出了问题,一开始我是这样写的:

void Java_com_xxx_test1(JNIEnv* env, jobject thiz, jint* javaArray, jint array_size) {
other_function((int *)javaArray, (int)array_size);
}

真正正确的写法应该是这样:

void Java_com_xxx_test1(JNIEnv* env, jobject thiz, jintArray* javaArray) {
// For C
jint* pJint = (*env)->GetByteArrayElements(env, javaArray, 0);
jsize array_size = (*env)->GetArrayLength(env, javaArray);
// For C++
// jint* pJint = env->GetByteArrayElements(javaArray, 0);
// jsize array_size = env->GetArrayLength(javaArray);
other_function((int *)pJint, array_size);
}

理论上讲这个问题应该是很容易发现的,但我为什么纠结了这么久呢,因为在之前我的另一段类似的代码中是工作正常的,像这样:

void Java_com_xxx_test2(JNIEnv* env, jobject thiz, jint* javaArray, jint array_size) {
int a[10]; // 假设 array_size 一定会小于10
int i;
for (int = 0; i < array_size; i++)
{
a[i] = (int)javaArray[i];
}
}

所以我一直认为第一种写法是正确的,害死人啊。

C回传数组给JAVA是这样做的:

jintArray Java_com_xxx_test3(JNIEnv* env, jobject thiz) {
int a = {1, 2, 3, 4};
jbyteArray result;
result = (*env)->NewByteArray(env, 4);
(*env)->SetIntArrayRegion(env, result, 0, 4, a);
// For C++
// result = env->NewByteArray(4);
// env->setIntArrayRegion(result, 0, 4, a);
return result
}

NDK note: the mangling of ‘va_list’ has changed in GCC 4.4

最近搞NDK,一切还算顺利,只是每次ndk-build都会提示:

In file included from jni/fskmodule.cpp:7:0:
/xxxx/usr/include/jni.h:592:13: note: the mangling of 'va_list' has changed in GCC 4.4

解决办法,修改 Android.mk,加一行:

LOCAL_CFLAGS = -Wno-psabi