Android NDK C++ Class undefined Reference - android

First.h
#ifndef FIRST_H
#define FIRST_H
class Test
{
public:
void create();
void test();
private:
};
#endif /* FIRST_H */
Second.cpp
#include "first.h"
#ifdef __cplusplus
extern "C" {
#endif
jint
Java_com_example_ndkcpp2_MainActivity_stringFromJNI( JNIEnv* env,
jobject thiz )
{
Test t;
t.test();
}
#ifdef __cplusplus
}
#endif
When I do a NDK-Build on second.cpp I got
pp2/jni/second.cpp:44: error: undefined reference to 'Test::test()'
collect2: ld returned 1 exit status

Using C++, you declare Class in .h file and write implementation in .cpp file. for example, you created First.h, then you should create First.cpp, in which write your method implementation like void Test::test(){}. Remember to add First.cpp to your makefile(Android.mk) for compilation.

You have multiple options here assuming that you have a first.cpp file that you implemented the Test class correctly. Without being able to see your Android.mk, I will go through all options:
Build First.cpp as a static or shared library and add this library to your module which compiles Second.cpp. Your Android.mk should look like:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := firstlib
LOCAL_C_INCLUDES := path/to/first.h
LOCAL_SRC_FILES := first.cpp
include $(BUILD_STATIC_LIBRARY)
If you would like First to be a shared library instead of a static library, change include $(BUILD_STATIC_LIBRARY) line to:
include $(BUILD_SHARED_LIBRARY)
Now, your second lib is compiled as follows:
include $(CLEAR_VARS)
LOCAL_MODULE := second
LOCAL_C_INCLUDES := path/to/first.h
LOCAL_C_INCLUDES += path/to/second.h
LOCAL_SRC_FILES := second.cpp
LOCAL_STATIC_LIBRARIES := firstlib
include $(BUILD_SHARED_LIBRARY)
If firstlib is built as shared library, you can link it by chaning LOCAL_STATIC_LIBRARIES += firstlib line to the following:
LOCAL_SHARED_LIBRARIES += firstlib
As a second solution, you can build first.cpp as a part of the second lib and this way you don't have to worry about linking against the first library. This is more like a design choice and how you would like to shape up your libraries:
include $(CLEAR_VARS
LOCAL_MODULE := libtwolib-second
include $(CLEAR_VARS)
LOCAL_MODULE := libtwolib-second
LOCAL_C_INCLUDES := path/to/first.h
LOCAL_C_INCLUDES += path/to/second.h
LOCAL_SRC_FILES := first.cpp
LOCAL_SRC_FILES += second.cpp
include $(BUILD_SHARED_LIBRARY)
Finally, you can find a sample of the first approach in your NDK directory, under samples/twolibs.

Related

Android.mk adding LOCAL_CPPFLAGS doesn't work

My source file can not include some header, because of local flag is not defined.
SSS.cpp:
#include <jni.h>
//This code is not defined:
#ifdef WORD
#include "Word.h"
#endif
//...rest of code
Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := word
LOCAL_CPPFLAGS := -DWORD
LOCAL_SRC_FILES := SSS.cpp
include $(BUILD_SHARED_LIBRARY)
And one more moment, project build was successful, but I can not run it because of lots of errors in source file (Eclipse c++ editor still can not see my header).
Probably not -WORD but -DWORD ?
-D defines a macro to be used by the preprocessor.
And what this
-std=c++11
belongs to?

Compile native C++ shared object with Android NDK

I have generated a .so file via C++ on Linus and got .so file generated now I have cross compile this .so file for Android ARM so I have to comiple it via Android NDK so that new genrated .so
can be used in my android project .
So can anyone help me where I have to put Linux generated .so file in my Android project and what to add in Make file(Android.mk) so that it can generate new .so file with existing methods in my previous Linux generated .so file.
I hope my question is clear to you all, if not please tell me.
Please help me. Thanks in advance
Here are the steps to build a native C++ shared object.
Add native support to your project. See Android's Add native support
Add your C++ files to the JNI folder
Create an Android.mk, add it to the the JNI folder, and add the architectures you want to support. For example:
APP_ABI := armeabi x86 mips armeabi-v7a
Pick a C++ runtime library. See the CPLUSPLUS.README in the NDK. I picked STL Port for the runtime (the GNU runtime was toxic due to its license).
Create an Appication.mk, add it to the the JNI folder, and add the magic. For example:
LOCAL_PATH := $(call my-dir)
# NDK_DEBUG_IMPORTS := 1
#########################################################
# STLport library
include $(CLEAR_VARS)
STLPORT_INCL := /opt/android-ndk-r9/sources/cxx-stl/stlport/stlport
STLPORT_LIB := /opt/android-ndk-r9/sources/cxx-stl/stlport/libs/$(TARGET_ARCH_ABI)
LOCAL_MODULE := stlport_shared
LOCAL_SRC_FILES := $(STLPORT_LIB)/libstlport_shared.so
LOCAL_EXPORT_CPPFLAGS :=
LOCAL_EXPORT_C_INCLUDES := $(STLPORT_INCL)
include $(PREBUILT_SHARED_LIBRARY)
LOCAL_SHARED_LIBRARIES := stlport_shared
#########################################################
# Crypto++ library
include $(CLEAR_VARS)
CRYPTOPP_INCL := /usr/local/cryptopp/android-$(TARGET_ARCH_ABI)/include
CRYPTOPP_LIB := /usr/local/cryptopp/android-$(TARGET_ARCH_ABI)/lib
LOCAL_MODULE := cryptopp
LOCAL_SRC_FILES := $(CRYPTOPP_LIB)/libcryptopp.so
LOCAL_EXPORT_CPPFLAGS := -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function
LOCAL_EXPORT_C_INCLUDES := $(CRYPTOPP_INCL) $(CRYPTOPP_INCL)/cryptopp
include $(PREBUILT_SHARED_LIBRARY)
LOCAL_SHARED_LIBRARIES := cryptopp
#########################################################
# PRNG library
include $(CLEAR_VARS)
APP_STL := stlport_shared
APP_MODULES := prng stlport_shared cryptopp
# My ass... LOCAL_EXPORT_C_INCLUDES is useless
LOCAL_C_INCLUDES := $(STLPORT_INCL) $(CRYPTOPP_INCL)
LOCAL_CPP_FEATURES := rtti exceptions
LOCAL_CPP_FLAGS := -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function
LOCAL_CPP_FLAGS += -Wl,--exclude-libs,ALL
LOCAL_LDLIBS := -llog -landroid
LOCAL_SHARED_LIBRARIES := cryptopp stlport_shared
LOCAL_MODULE := prng
LOCAL_SRC_FILES := libprng.cpp
include $(BUILD_SHARED_LIBRARY)
My library depends on STLport. The stuff following "STLport library" ensures my library is compiled against stlport_shared.so, and the stlport_shared.so is copied into the APK.
My library also depends upon a cross-compiled version of Crypto++. Crypto++ was also compiled/linked against stlport_shared.so. The stuff following "Crypto++ library" ensures my library is compiled against libcryptopp.so, and the libcryptopp.so is copied into the APK.
Finally, my library is called out. My library is the stuff following "PRNG library" (its a one file test project). It builds libprng.so, and ensures libprng.so is copied into the APK.
You'll also need Android classes. Here's what mine looks like.
package com.cryptopp.prng;
public class PRNG {
static {
System.loadLibrary("stlport_shared");
System.loadLibrary("cryptopp");
System.loadLibrary("prng");
}
private static native int CryptoPP_Reseed(byte[] bytes);
private static native int CryptoPP_GetBytes(byte[] bytes);
private static Object lock = new Object();
// Class method. Returns the number of bytes consumed from the seed.
public static int Reseed(byte[] seed) {
synchronized (lock) {
return CryptoPP_Reseed(seed);
}
}
// Class method. Returns the number of bytes generated.
public static int GetBytes(byte[] bytes) {
synchronized (lock) {
return CryptoPP_GetBytes(bytes);
}
}
// Instance method. Returns the number of bytes consumed from the seed.
public int reseed(byte[] seed) {
synchronized (lock) {
return CryptoPP_Reseed(seed);
}
}
// Instance method. Returns the number of bytes generated.
public int getBytes(byte[] bytes) {
synchronized (lock) {
return CryptoPP_GetBytes(bytes);
}
}
}
The Android modified build system really sucks. Its sufficiently different from standard make-based projects and poorly documented. But that's what Android offers, so that's what you have to use. Eclipse's Android native support is built around it.
If interested, here's what the wrapper header file looks like. You can use javah to generate it from your DEX file (compiled Java classes).
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_cryptopp_prng_PRNG */
#ifndef _Included_com_cryptopp_prng_PRNG
#define _Included_com_cryptopp_prng_PRNG
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_cryptopp_prng_PRNG
* Method: CryptoPP_Reseed
* Signature: ([B)I
*/
JNIEXPORT jint JNICALL Java_com_cryptopp_prng_PRNG_CryptoPP_1Reseed
(JNIEnv *, jclass, jbyteArray);
/*
* Class: com_cryptopp_prng_PRNG
* Method: CryptoPP_GetBytes
* Signature: ([B)I
*/
JNIEXPORT jint JNICALL Java_com_cryptopp_prng_PRNG_CryptoPP_1GetBytes
(JNIEnv *, jclass, jbyteArray);
#ifdef __cplusplus
}
#endif
#endif

Android JNI Using Pre built .so file in C Code

I am trying my hand at Android JNI.
So far I have just written the basic Hello World Android JNI App.
Now I was thinking if it would be possible to build a .so file seperately. Then use that library in my JNI Layer C Code.
So basically I want to build a libsample.so using command line gcc in windows. Then use this .so file in the JNI I write for my android app.
So far from my understanding I should be able to do this by editing the Android.mk file.
But what would those edits be ?
EDIT: Source code attached.
jnitest.cpp -
#include <jni.h>
#include "specialPrint.h"
extern "C" jstring Java_com_example_hwjni_MainActivity_helloFromJni(JNIEnv *env, jobject thiz) {
return env->NewStringUTF(SpecialPrint("This is external .so talking."));
}
specialPrint.h -
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
char* SpecialPrint(char* s);
#ifdef __cplusplus
}
#endif
specialPrint.c -
#include <stdio.h>
#include "specialPrint.h"
char* SpecialPrint(char* s) {
return s;
}
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := MyPrebuiltLib
LOCAL_SRC_FILES = specialPrint.c
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := jnitest
LOCAL_SRC_FILES := jnitest.cpp
LOCAL_SHARED_LIBRARIES := MyPrebuiltLib
include $(BUILD_SHARED_LIBRARY)
P.S. - Although right now I am trying to do this with the source. But eventually I really need to do this stuff without the source of specialPrint. If you could help me achive that then it would be great!
Step 1: declare a prebuilt library as module
Step 2: refer the prebuilt module as local shared library
for example
include $(CLEAR_VARS)
LOCAL_MODULE := MyPrebuiltLib
LOCAL_SRC_FILES = path/to/libSuper.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := DroidJNILib
LOCAL_SRC_FILES := AwsomeCode.cpp
LOCAL_SHARED_LIBRARIES := MyPrebuiltLib
include $(BUILD_SHARED_LIBRARY)
If you have the source
include $(CLEAR_VARS)
LOCAL_MODULE := MyPrebuiltLib
LOCAL_SRC_FILES =mysource.cpp
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := DroidJNILib
LOCAL_SRC_FILES := AwsomeCode.cpp
LOCAL_SHARED_LIBRARIES := MyPrebuiltLib
include $(BUILD_SHARED_LIBRARY)

Android NDK: Static library used is different than precompiled library specified

I build a C++ library manually using the Android SDK compiler. The result is libMyUtils.a.
I'm using the following in my Java/JNI test application:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := MyUtils
LOCAL_SRC_FILES := ../../../../libs/libMyUtils.a
include $(PREBUILT_STATIC_LIBRARY)
LOCAL_MODULE := AndroidTests
LOCAL_STATIC_LIBRARIES := MyUtils
include $(BUILD_SHARED_LIBRARY)
When I build the console shows the following:
[armeabi] Install : libMyUtils.so => libs/armeabi/libMyUtils.so
Now for some bizarre reason the library ../../../../libs/libMyUtils.a is a couple megabytes, but the library libs/armeabi/libMyUtils.so is only 5KB. Isn't it supposed to be the same library?
When I run my test application I get UnsatisfiedLinkError. Obviously the native function I'm calling isn't located in the library. What am I doing wrong?
A full explanation, as your Android.mk does not make much sense to me: sorry if you already know some of that.
Static libraries should be pure C/C++, and wrapped using the Android NDK in order to be usable from Java.
For example, assuming your static library is built from a simple .c and .h files:
highfive.h:
int giveMeFive();
highfive.c:
#include "highfive.h"
int giveMeFive() {
return 5;
}
This can be compiled as a static library using the Android NDK compiler, which apparently you already know how to do: this will give us a highfive.a library.
In this form, this library is unusable from Java, but it can be wrapped using the Android NDK. See the Android NDK documentation for naming conventions etc...
highfiveWrapper.c:
#include "highfive.h"
jint
Java_your_package_name_HighFive_giveMeFive(JNIEnv *env, jobject o) {
return (jint) giveMeFive();
}
and its corresponding Java file:
package your.package.name;
class HighFive {
static {
System.loadLibrary("highfive");
}
public native int giveMeFive();
}
Now, how do we compile all this to get it to work:
Android.mk:
include $(CLEAR_VARS)
LOCAL_MODULE := libhighfive-prebuilt
LOCAL_SRC_FILES := path/to/highfive.a
LOCAL_EXPORT_C_INCLUDES := path/to/highfive.h/folder
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := highfive
LOCAL_SRC_FILES := path/to/highfiveWrapper.c
LOCAL_STATIC_LIBRARIES := libhighfive-prebuilt
include $(BUILD_SHARED_LIBRARY)
And there you should be able to use your native library as you wished to!
Hope this helps!
Usually, static libraries contain many objects, which contain many functions, of which many are unused. That's why the linker only pulls the referenced objects from static libraries. So, in the example given by #mbrenon, highfiveWrapper.c defines which components of highfive.a will be linked into highfive.so.
But in your setup, you need whole static library to be loaded. OK, there is a special word for it: LOCAL_WHOLE_STATIC_LIBRARIES.
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := MyUtils
LOCAL_SRC_FILES := ../../../../libs/libMyUtils.a
include $(PREBUILT_STATIC_LIBRARY)
LOCAL_MODULE := AndroidTests
LOCAL_WHOLE_STATIC_LIBRARIES := MyUtils
include $(BUILD_SHARED_LIBRARY)
In the end the solution was to build the MyUtils library as a prebuilt shared library. This way the linker doesn't strip anything. Then I modified my makefile as follows:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := MyUtils
LOCAL_SRC_FILES := ../../../../libs/libMyUtils.so
include $(PREBUILT_SHARED_LIBRARY)
LOCAL_MODULE := AndroidTests
LOCAL_SHARED_LIBRARIES := MyUtils
include $(BUILD_SHARED_LIBRARY)
Notice the .so extention and the PREBUILT_SHARED_LIBRARY and BUILD_SHARED_LIBRARY scripts.

Link shared library under Android NDK

I with success compile library LibXtract to shared object libxtract.so and want to use is in second project.
In mention project I try to compile it on simple function:
#include <com_androidnative1_NativeClass.h>
#include <android/log.h>
#include "libxtract.h"
JNIEXPORT void JNICALL Java_com_androidnative1_NativeClass_showText
(JNIEnv *env, jclass clazz)
{
float mean = 0, vector[] = {.1, .2, .3, .4, -.5, -.4, -.3, -.2, -.1}, spectrum[10];
int n, N = 9;
float argf[4];
argf[0] = 8000.f;
argf[1] = XTRACT_MAGNITUDE_SPECTRUM;
argf[2] = 0.f;
argf[3] = 0.f;
xtract[XTRACT_MEAN]((void *)&vector, N, 0, (void *)&mean);
__android_log_print(ANDROID_LOG_DEBUG, "LIbXtract", "Button pushe2");
}
I have flat structure:
jni/com_androidnative1_NativeClass.c
jni/com_androidnative1_NativeClass.hjni/libxtract.h
jni/other *.h files from libxtract interface
jni/Android.mk
jni/Applicatoin.mk
library libxtract.so I put in mainproject/lib folder
my Android.mk file looks like:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := com_androidnative1_NativeClass.c
LOCAL_MODULE := com_androidnative1_NativeClass
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/
LOCAL_LDLIBS += -llog
LOCAL_SHARE_LIBRARIES := libxtract
NDK_MODULE_PATH += $(LOCAL_PATH)/../lib/
include $(BUILD_SHARED_LIBRARY)
and I still got error:
Compile thumb : com_androidnative1_NativeClass <= com_androidnative1_NativeClass.c
SharedLibrary : libcom_androidnative1_NativeClass.so./obj/local/armeabi/objs/com_androidnative1_NativeClass/com_androidnative1_Nativ eClass.o: In function `Java_com_androidnative1_NativeClass_showText':
/home/jack/Projects/AndroidNative1/jni/com_androidnative1_NativeClass.c:20: undefined reference to `xtract'
collect2: ld returned 1 exit status
make: *** [obj/local/armeabi/libcom_androidnative1_NativeClass.so] Error 1
Code came form example of LibXtract and under C++ compile without problems, any ideas?
Your Android make file should be ...
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LIB_PATH := $(LOCAL_PATH)/../lib
LOCAL_SRC_FILES := com_androidnative1_NativeClass.c
LOCAL_MODULE := com_androidnative1_NativeClass
LOCAL_LDLIBS += -llog
LOCAL_LDLIBS += $(LIB_PATH) -lxtract
LOCAL_SHARE_LIBRARIES := libxtract
include $(BUILD_SHARED_LIBRARY)
Try this make file in your second project, and you can successfully build your code without having any error.
In the above answer all is right but exept one.
When we want to link lib we must add -L before LOCAL_LDLIBS dir as below.
LIB_PATH := $(LOCAL_PATH)/../lib
LOCAL_LDLIBS += **-L**$(LIB_PATH) -lxtract
Else it will give error as below
cannot open XXX/../lib: Permission denied
You need to tell Android NDK build scripts about your shared library. Check ${NDK}/doc/PREBUILTS.html for instructions how this can be done. They advise to add Android.mk in the same directory where you have your libXtract.so:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libXtract
LOCAL_SRC_FILES := libXtract.so
include $(PREBUILT_SHARED_LIBRARY)
Debugging tip: I guess you are using ndk-build to build your "second project". Try running ndk-build with V=99 (try V=99 ndk-build or ndk-build V=99 - my memory failing). This will show you the the exact failing linking command. You should likely have options -lXtract and -L/path/to/libXtract/library. (Sometimes it is convenient to just copy and paste the linking command to run it manually to find the right options for successful linking, before actually fixing the build settings.)
Update: I now see #codetiger's comment seems to point to a same sort of answer (without mentioning the NDK document which is good reading - so I am not deleting this answer).

Categories

Resources