Please help I search throw whole internet but I can't find answer ...
C Layer
I have created simple function int mean(int, int);
and place it in calc_mean.h and initialize it in calc_mean.c here are this two files.
calc_mean.c
开发者_如何学编程#include "calc_mean.h"
int mean(int a, int b)
{
return (a+b) / 2;
}
calc_mean.h
int mean(int, int);
Then I create make file and build archive (.a) file called Test_Archive.a using following make file
makefile
GCC := C:\Tools\ndk-toolchain\ndk-standalone\bin\arm-linux-androideabi-gcc.exe
GPP := C:\Tools\ndk-toolchain\ndk-standalone\bin\arm-linux-androideabi-g++.exe
AR := C:\Tools\ndk-toolchain\ndk-standalone\bin\arm-linux-androideabi-ar.exe
default: all
all: obj
$(AR) r Test_Archive.a *.o
obj:
$(GPP) -c *.c
I get Test_Archive.a archive file. Now I want to add this archive file to JNI and call mean
function from my JNI project, for that I have created JNI and Java files which you can see below.
JAVA Layer
How you can see here in java layer I have TestJavaClass that have one native method called JMean
this method have two int
arguments and return also int
.
public class TestJavaClass
{
/** Default Constructor
*
*/
public TestJavaClass( ) {
}
/** Test Function.
*
* @param a
* @param b
* @return
*/
public native int JMean( int a, int b);
}
JNI Layer
This is JNI header file
Test_Library.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_Fido_OSTXLib_OSTX */
#ifndef _Included_com_Fido_OSTXLib_OSTX
#define _Included_com_Fido_OSTXLib_OSTX
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_Fido_OSTXLib_OSTX
* Method: JMean
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_Fido_OSTXLib_OSTX_JMean
(JNIEnv *, jobject, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
Test_Library.c
#include "Test_Library.h"
#include "calc_mean.h"
JNIEXPORT jint JNICALL Java_com_Fido_OSTXLib_OSTX_JMean( JNIEnv* env, jobject thiz, jint a, jint b )
{
return mean( a, b );
}
I put this two files Test_Library.h, Test_Library.c and calc_mean.h, Test_Archive.a and create make file Android.mk
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := Test_Library
LOCAL_SRC_FILES := Test_Library.c
LOCAL_LDLIBS := -LTest_Archive.a
include $(BUILD_SHARED_LIBRARY)
but when I want to create .so file, the following error occurs, why ?
$ ../../ndk-build Compile thumb : Test_Library <= Test_Library.c SharedLibrary : libTest_Library.so C:/cygwin/home/android-ndk-r5b/Fido/ProjectOSTX/obj/local/armeabi/objs/OSTX_Library/OSTX_Library.o: In function
Java_com_Fido_OSTXLib_OSTX_JMean': C:/cygwin/home/android-ndk-r5b/Fido/ProjectOSTX/jni/OSTX_Library.c:6: undefined reference to
mean' collect2: ld returned 1 exit status make: * [/home/android-ndk-r5b/Fido/ProjectOSTX/obj/local/armeabi/libOSTX_Library.so] Error 1
Why undefined reference to `mean' ?
Here is what I did to build the code successfully:
In your Eclipse Android Project create the folder
jni
.Add the files
calc_mean.c; calc_mean.h; Test_Library.c and Android.mk
to the jni folderRemove the line
#include "Test_Library.h"
from the Test_Library.c file. Everything else can stay as it is.In the cygwin command line go to the root folder of your Eclipse Android project and run
ndk-build
If you want to build your calc_mean also as static library during the ndk-build process then you can change your Android.mk file as follows:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := Test_Archive
LOCAL_SRC_FILES := calc_mean.c
# compiler flags.
LOCAL_LDLIBS = -lz -lm
LOCAL_CFLAGS = -Wall -pedantic -std=c99 -g
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := Test_Library
LOCAL_STATIC_LIBRARIES := Test_Archive
LOCAL_SRC_FILES := Test_Library.c
include $(BUILD_SHARED_LIBRARY)
As I suppos the main reason was compiler settings, I make some modifications in makefile and my .a archive linked to shared library, here are modifications.
GCC := C:\Tools\ndk-toolchain\ndk-standalone\bin\arm-linux-androideabi-gcc.exe
GPP := C:\Tools\ndk-toolchain\ndk-standalone\bin\arm-linux-androideabi-g++.exe
AR := C:\Tools\ndk-toolchain\ndk-standalone\bin\arm-linux-androideabi-ar.exe
OPTIONS :=\
-fpic \
-ffunction-sections \
-funwind-tables \
-fstack-protector \
-D__ARM_ARCH_5__ \
-D__ARM_ARCH_5T__ \
-D__ARM_ARCH_5E__ \
-D__ARM_ARCH_5TE__ \
-Wno-psabi \
-march=armv5te \
-mtune=xscale \
-msoft-float \
-mthumb \
-Os \
-fomit-frame-pointer \
-fno-strict-aliasing \
-finline-limit=64 \
-DANDROID \
-Wa, \
-O2 \
-DNDEBUG \
-g \
default: all
all: obj
$(AR) r libtestlibrary.a *.o
obj:
$(GCC) $(OPTIONS) -c *.c
Simply use C compiler instead!
You compile with C++ compiler (Makefile has "$(GPP) -c *.c") and the function symbol name includes parameter information and is called different: not "mean", but something more complex, similar as Java names symbols internally. You can use "nm" to see what's inside an object file.
For example my g++ generates:
# nm x.o
00000000 T _Z4meanii
So there is no "mean" (but "Z4meanii"). Using a C compiler, and the symbol names are straightforward:
# nm x.o
00000000 T mean
(Please note that the same source code content was compiled)
So change in you Makefile:
obj:
$(GCC) -c *.c
If for some reason a C++ compiler must be used, then extern "C" must be added:
calc_mean.h (simplified, without inclusion guards etc.)
#ifdef __cplusplus
extern "C" {
#endif
int mean(int, int);
#ifdef __cplusplus
} // extern "C"
#endif
In general, if you need C symbol names in C++ files, use extern "C". Test_Library.h uses it correctly and provides a nice example. You can put the same extern "C" logic (best including if #ifdef __cplusplus block) to your header file. Some consider it good practice to always do so.
But note that there are several, sometimes subtle, differences between C and C++, so I recommend to compile C sources with C compiler, although I know that there can be advantages using a C++ compiler.
oki,
Steffen
精彩评论