I want to use exiv2 library written in C++ in my Android project. To do that I try to cross-compile the library using Android NDK.
For cross-compiling I follow presented below steps:
Add the ndk path to variable PATH
$ PATH="/home/patrycja/android-packages/ndk:${PATH}"
$ export PATH
Install the standard toolchain for cross-compiling C/C++ for Android.
./make-standalone-toolchain.sh --platform=android-21 --install-dir=/tmp/my-android-toolchain --ndk-dir='/home/patrycja/android-packages/ndk/' --toolchain=arm-linux-androideabi-4.9 --system=linux-x86_64
Copying prebuilt binaries...
Copying sysroot headers and libraries...
Copying c++ runtime headers and libraries...
Copying files to: /tmp/my-android-toolchain
Cleaning up...
Done.
Set some environment variables so that the configuration and build process will use the right compiler.
$ export PATH=/tmp/my-android-toolchain/bin:$PATH
$ export CC="arm-linux-androideabi-gcc"
$ export CXX="arm-linux-androideabi-g++"
$ export CFLAGS='-mthumb -O2'
$ export CXXFLAGS='-mthumb -O2'
$ export LDFLAGS='-Wl,--fix-cortex-a8'
$ export LIBS='-lstdc++ -lsupc++'
Bulid the static library and sufficient headers
./configure --prefix=$(pwd)/build --host=arm-linux-androideabi --disable-shared --disable-xmp --disable-nls
As a result I have in created ‘build’ category files:
├── bin
│ └── exiv2
├── include
│ └── exiv2
│ ├── *.hpp
│
├── lib
│ ├── libexiv2.a
│ ├── libexiv2.la
│ └── pkgconfig
│ └── exiv2.pc
└── share
└── man
└── man1
└── exiv2.1
I copied created static library libexiv2.a
and include
folder to my Android project in appName/src/main/jni/prebuild
.
Android.mk
looks like:
LOCAL_PATH := $(call my-dir)
#static library info
LOCAL_MODULE := exiv2
LOCAL_SRC_FILES := ../prebuild/libexiv2.a
LOCAL_EXPORT_C_INCLUDES := ../prebuild/include/
LOCAL_EXPORT_LDLIBS := -lz
include $(PREBUILT_STATIC_LIBRARY)
#wrapper info
include $(CLEAR_VARS)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../prebuild/include/
LOCAL_MODULE := helloJNI
LOCAL_SRC_FILES := helloJNI.cpp
LOCAL_STATIC_LIBRARIES := exiv2
include $(BUILD_SHARED_LIBRARY)
In my wrapper in Android I try to use the library. It looks like follows:
#include <string.h>
#include <jni.h>
#include <exiv2/exiv2.hpp>
extern "C" {
JNIEXPORT jstring JNICALL Java_com_example_patrycja_testndi2_MyActivity_helloJNI(JNIEnv *env, jobject thiz)
{
std::ostringstream os;
std::string file("/storage/emmc/DCIM/100MEDIA/IMAG0021.jpg");
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(file);
return env->NewStringUTF("asldjaljd");
}
}
However ndk-build
outputs that it can’t find it.
[arm64-v8a] Compile++ : helloJNI <= helloJNI.cpp
[arm64-v8a] SharedLibrary : libhelloJNI.so
jni/../prebuild/libexiv2.a: error adding symbols: File in wrong format
collect2: error: ld returned 1 exit status
make: *** [obj/local/arm64-v8a/libhelloJNI.so] Error 1
I believe that there’s something wrong with flags in cross-compiling. I have tried several options but something is still wrong. I’ve followed these intructions: https://groups.google.com/forum/#!topic/android-ndk/mYh1LzMu_0U
you've compiled exiv2 for armv5+ devices running at least Lollipop. Here ndk-build fails because it's trying to link it from an arm64-v8a library it's building.
Cross compiling without using ndk-build is hard to get right on Android, especially as you should support not only armv5, but also armv7, x86, x86_64, arm64-v8a...
You should first set the --platform
option to the same level than your minimum SDK level. Then rebuild your lib and place it under ../prebuild/armeabi
.
Then cross compile your lib for also x86 architecture:
./make-standalone-toolchain.sh --platform=android-9 --install-dir=/tmp/my-android-toolchain-x86 --ndk-dir='/home/patrycja/android-packages/ndk/' --arch=x86 --toolchain=x86-4.8 --system=linux-x86_64
$ export PATH=/tmp/my-android-toolchain-x86/bin:$PATH
$ export CC="i686-linux-android-gcc"
$ export CXX="i686-linux-android-g++"
$ export CFLAGS='-O2 -mtune=atom -mssse3 -mfpmath=sse'
$ export CXXFLAGS='-O2 -mtune=atom -mssse3 -mfpmath=sse'
$ export LDFLAGS=''
$ export LIBS='-lstdc++ -lsupc++'
./configure --prefix=$(pwd)/build-x86 --host=x86 --disable-shared --disable-xmp --disable-nls
and move the created .a to ../prebuild/x86
.
Ideally, you should repeat the same process also for armeabi-v7a, mips, mips64, arm64-v8a.
Finally, you can include the right .a inside your Android.mk using the TARGET_ARCH_ABI
variable, like so:
LOCAL_PATH := $(call my-dir)
#static library info
LOCAL_MODULE := exiv2
LOCAL_SRC_FILES := ../prebuild/$(TARGET_ARCH_ABI)/libexiv2.a
LOCAL_EXPORT_C_INCLUDES := ../prebuild/include/
LOCAL_EXPORT_LDLIBS := -lz
include $(PREBUILT_STATIC_LIBRARY)
#wrapper info
include $(CLEAR_VARS)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../prebuild/include/
LOCAL_MODULE := helloJNI
LOCAL_SRC_FILES := helloJNI.cpp
LOCAL_STATIC_LIBRARIES := exiv2
include $(BUILD_SHARED_LIBRARY)
and inside Application.mk (create a new file if it doesn't exist), specify the architectures you're supporting and the minimum platform you're targeting:
APP_ABI := armeabi x86 # ideally, this should be set to "all"
APP_PLATFORM := android-14 # should the same as -platform and your minSdkVersion.