利用Cmake以及NDK-Standalone toolchain生成静态库用于android C++ 文件~

利用Cmake以及NDK-Standalone toolchain生成静态库用于Android C++ 文件~

  • 利用Cmake以及NDK-Standalone toolchain生成静态库用于Android C 文件
    • Introduce
    • Build NDK Standalone toolchain
    • Use Cmake to build a static library
    • Build a static library for Android
    • Reference

本博客从某种意义上起着一个备忘录的作用。但是如果能帮助到因为某些问题而在百度上到处乱撞而一无所获的你,也是我本来的初衷。又: 我拿英文写了啊。英文这个东西嘛,要多看才能看的快。所以但愿你不会有抵触情绪~~

1. Introduce

I am tring to use SiftGPU in Android. Though I haven’t reached my goal yet, I have figured out how to build a static library using standalone toolchain together with cmake and use it in the Android project. Here is the process:

2. Build NDK Standalone toolchain

Currently I am using the Ubuntu 17.04. First, download the android-ndk-r15c-linux-x86_64.zip from here: https://developer.android.com/ndk/downloads/index.html

Extract it and enter the android-ndk-r15c folder, run the following command:

sudo sh ./build/tools/make-standalone-toolchain.sh --verbose --platform=android-15 
--install-dir=/home/YourUserName/Downloads/my-tool --toolchain=arm-linux-androideabi-4.9

Here is the explaination of this command:

--verbose: show debug messages
--platform=android-15: select the version of Android
--install-dir=/home/YourUserName/Downloads/my-tool:
Notice that you should change "YourUserName" to your own user name. 
In my case, it will be generated under my-tool folder. 
--toolchain=arm-linux-androideabi-4.9:
The standalone-toolchain files will be generated under this folder. 
You can refer to this article to select the toolchain you need:

https://developer.android.com/ndk/guides/standalone_toolchain.html

3. Use Cmake to build a static library

Then, We are going to use cmake and build a static library. Note that the ndk standalone toolchain is not used in this step. First, you can check your cmake version by the following command:

cmake --version

Mine is:

 cmake version 3.7.2. 

Then, we can build a simple cpp file:

// main.cpp
#include 
#include "Structure.h"
int main() {
    int a = 8;
    int b = 9;
    int c = sum( a, b );
    printf( "sum of %d and %d is %d\n", a, b, c );
    return 0;
}

After that, we can also build the header file of our function:

//Structure.h
int sum( int a, int b );

And the c++ file of our function:

//Structure.cpp
int sum( int a, int b ) { 
    return a + b;
}

Now we are going to write our very first CmakeLists.txt. It is going to build a static library. And our main.cpp file is going to use the static library:

CMAKE_MINIMUM_REQUIRED(VERSION 2.6)

project(HelloWorld)

# CMake instructions to make the static lib

ADD_LIBRARY( MyStaticLib STATIC
             Structure.cpp )

# CMake instructions to test using the static lib

SET( APP_EXE StaticTest )

ADD_EXECUTABLE( ${APP_EXE}
                main.cpp ) 

TARGET_LINK_LIBRARIES( ${APP_EXE}
                       MyStaticLib )

Just run the following commands:

mkdir my_pure_build
cd my_pure_build
cmake ..
make 
./StaticTest

If everything goes well, you will see this in your terminal:

sum of 8 and 9 is 17

4. Build a static library for Android

In this section, we are going to use ndk together with cmake to build a static library for Android. If we run the following command:

readelf --file-header StaticTest

We can get:

ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x760
  Start of program headers:          64 (bytes into file)
  Start of section headers:          6632 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         9
  Size of section headers:           64 (bytes)
  Number of section headers:         29
  Section header string table index: 28

Note that the value of Machine is “Advanced Micro Devices X86-64” , It means that it is not for Android device, which is “ARM” instead.

So, how can we generate a static library for “ARM”? We just need to change our CmakeLists.txt like this:

CMAKE_MINIMUM_REQUIRED(VERSION 2.6)

project(HelloWorld)

#please change "YourUserName" to your own user name~
set(NDK_STANDALONE_TOOLCHAIN /home/YourUserName/Downloads/my-tool/)
set(CMAKE_SYSTEM_NAME Android)
#choose Android version. This is not the version of Cmake.
set(CMAKE_SYSTEM_VERSION 15)
set(CMAKE_C_COMPILER ${NDK_STANDALONE_TOOLCHAIN}/bin/arm-linux-androideabi-gcc)
set(CMAKE_CXX_COMPILER ${NDK_STANDALONE_TOOLCHAIN}/bin/arm-linux-androideabi-g++)
#choose Android ABI. Mine is armeabi-v7a
set(CMAKE_ANDROID_ARCH_ABI armeabi-v7a)
set(CMAKE_FIND_ROOT_PATH ${NDK_STANDALONE_TOOLCHAIN})
add_definitions("--sysroot=${NDK_STANDALONE_TOOLCHAIN}/sysroot")

# CMake instructions to make the static lib

ADD_LIBRARY( MyStaticLib STATIC
             Structure.cpp )

# CMake instructions to test using the static lib

SET( APP_EXE StaticTest )

ADD_EXECUTABLE( ${APP_EXE}
                main.cpp ) 

TARGET_LINK_LIBRARIES( ${APP_EXE}
                       MyStaticLib )

Then, use the following code to create a new folder, enter it, build a new static library, and test the result.

mkdir my_ndk_build
cd my_ndk_build
cmake ..
make 
./StaticTest

You will get this result:

bash: ./StaticTest: cannot execute binary file: Exec format error

If you run this command:

readelf --file-header StaticTest

You can get:

ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           ARM
  Version:                           0x1
  Entry point address:               0x8d94
  Start of program headers:          52 (bytes into file)
  Start of section headers:          41596 (bytes into file)
  Flags:                             0x5000200, Version5 EABI, soft-float ABI
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         9
  Size of section headers:           40 (bytes)
  Number of section headers:         36
  Section header string table index: 35

Very good. The value of Machine is “ARM” now. Of course you cannot run it in your linux system, or, in your laptop. Instead, you are going to run it in your Android device!

However, you may wonder, how can you do that? Actually this is a rather complex topic if you have no idea about what ndk is, and how to compile c++ on Android platform. But as you have found this article and read it even though it is written in English, I think I can assume that you know what I am saying. Otherwise, I strongly recommand you watching this vedio and learn about ndk. You may need a before start learning, but this is a really good tutorial and it can prepare you for the ndk and OpenCV for Android: https://www.youtube.com/watch?v=Oq3oiCfSgbo

To use the libMyStaticLib.a we have just got in Android, just copy libMyStaticLib.a together with Structure.h to the jni folder in your Android project. Feel free to create one if you don’t have the “jni” folder. Note that I am using the Android Studio rather than eclipse.

And the Android.mk is also under the jni folder:

LOCAL_PATH  := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := MyStaticLib
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
LOCAL_SRC_FILES := libMyStaticLib.a
include $(PREBUILT_STATIC_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE    := TrueSiticher
LOCAL_SRC_FILES := libTrueSiticher.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
OPENCV_ROOT := C:\Users\dell\Downloads\opencv-2.4.13.2\platforms\android_new_ocl\install
OPENCV_INSTALL_MODULES := on
OPENCV_CAMERA_MODULES  := on
OPENCV_LIB_TYPE:=SHARED
include ${OPENCV_ROOT}/sdk/native/jni/OpenCV.mk

LOCAL_SRC_FILES := com_example_dell_mycv_OpenCVGray.cpp

LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog -ldl -lz

LOCAL_MODULE := MyOpencvStitcher

LOCAL_SHARED_LIBRARIES += TrueSiticher

LOCAL_STATIC_LIBRARIES += MyStaticLib

include $(BUILD_SHARED_LIBRARY)

Well, If you are clever enough, you can also learn how to add shared library and use OpenCV on Android from the Android.mk above. However, that’s another story. Let’s just focus on the static library this time.

To use the function in the library, your c++ file should be something like this:

#include <com_example_dell_mycv_OpenCVGray.h>
#define LOG    "MyCV" // Set LOG by yourself
#define LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG,__VA_ARGS__)//For output
#include <Structure.h>
JNIEXPORT jint JNICALL Java_com_example_dell_mycv_OpenCVGray_convertGray
  (JNIEnv *, jclass, jlong addrRgba, jlong addrGray){

    int sum_result = sum(5,2);
    __android_log_print(ANDROID_LOG_INFO, LOG, "geneus result is %d", sum_result);

    jint retval;
    retval = (jint)conv;
    return retval;
}

After you have run your app, we can expect result like this in the logcat:

geneus result is 7

Then you are done. Thanks for reading.

5. Reference

[1]. https://lockephan.wordpress.com/2014/01/29/compiling-your-android-native-code-project-using-cmake/
[2]. http://mobilepearls.com/labs/native-android-api/ndk/docs/STANDALONE-TOOLCHAIN.html
[3]. https://stackoverflow.com/questions/43772887/cmake-creating-a-static-library

稍后(在接下来的几天,或者就是明天),我会把教程的细节再完善一下。

你可能感兴趣的:(android-教程)