Android NDK Development (I) Development Process in Android Studio 3.0 Environment

Keywords: cmake Android Java Gradle

cover

In the first article of 2018, I mainly explained some concepts of JNI and NDK and how to use CMake build tools to develop NDK in AS 3.0 environment.

1. Understand some concepts

  • JNI(Java Native Interface):

    Java native interface is a bridge between Java and other native code languages (such as C and C++).

  • NDK(Native Development Kit):

    Native Development Toolset is a set of tools that allow you to implement program functions using native code languages such as C and C++.

  • ABI(Application Binary Interface):

    Application binary interface, different CPUs support different instruction sets, each combination of CPU and instruction set has its own application binary interface (or ABI), ABI can very accurately define how the machine code of application interacts with the system at run time.

    ABI Official Documents

    Supported ABI: armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, mips, mips64

  • CMake:

    The NDK build tool recommended by Android has been supported since AS 2.2 (including version 2.2).

2. Environmental Construction

Install the tools needed for NDK development

Install the tools needed for NDK development

Install the following components in SDK Tools:

  • Cmake: NDK Construction Tool

  • LLDB: NDK Debugging Tool

  • NDK: NDK Development Toolset

Create NDK project

Create NDK project

When creating a project, check the Include C++ support option, and then go all the way to the Customize C++ Support settings page:

Customize C++ Support

You can see three options:

  • C++ Standard: C++ Standard. Selecting Toolchain Default will use the default Make configuration.

  • Exceptions Support: Supports C++ exception handling, marked - fexceptions.

  • Runtime Type Information Support: Supports runtime type recognition, marked as
    - frtti, which allows programs to use pointers or references to base classes to check the actual derived types of the pointers or references to the objects they refer to.

Here we use the default C++ standard, do not check the following two options, click the [Finish] button to enter the next link.

3.NDK project

Look at the project catalogue:

Project directory

In the figure above, the difference between NDK project and common project is marked with red boxes. Let's take a look at the following:

First, let's look at the build.gradle configuration:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "com.yl.ndkdemo"
        minSdkVersion 15
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags ""
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}

dependencies {
    // Reference to some libraries
}

You can see two more externalNativeBuild configuration items in the build.gradle configuration:

  • In defaultConfig:

    Mainly configure the command parameters of Cmake, for example, when creating a project, if the Exceptions Support and Runtime Type Information Support options are checked, the configuration is as follows:

externalNativeBuild {
    cmake {
        cppFlags "-fexceptions -frtti"
    }
}

More command parameters can be viewed Android NDK CMake document

  • Outside defaultConfig:

    This paper mainly defines the path of CMake construction script CMakeLists.txt.

CMake build script CMakeLists.txt

CMakeLists.txt is the construction script of CMake, which is equivalent to Android.mk in ndk-build. Look at CMakeLists.txt:

# Setting the Minimum Version of Cmake
cmake_minimum_required(VERSION 3.4.1)

# Compile library
add_library( # Set the library name
             native-lib

             # Setting up the library mode
             # SHARED mode compiles so files, STATIC mode does not.
             SHARED

             # Setting Native Code Path
             src/main/cpp/native-lib.cpp )

# Location library
find_library( # The name of library
              log-lib

              # Store the library path as a variable that can be used elsewhere to reference the NDK Library
              # Set the variable name here
              log )

# Associated library
target_link_libraries( # Associated library
                       native-lib

                       # Associate native-lib with log-lib
                       ${log-lib} )

This is a basic CMake build script. For more script configurations, please refer to CMAKE manual I can't understand it! That's OK. Here's the Chinese version. CMAKE Manual - Chinese Version.

Native code native-lib.cpp

Android provides a simple JNI interactive Demo that returns a string to the Java layer. The method name is named by Java_package name_class name_method name:

#include <jni.h>
#include <string>

extern "C"
JNIEXPORT jstring

JNICALL
Java_com_yl_ndkdemo_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

See how to call:

public class MainActivity extends AppCompatActivity {

    // Load native-lib without lib prefix
    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Display the acquired string on TextView
        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());
    }

    /**
     * native-lib Native method in
     */
    public native String stringFromJNI();
}

The invocation method is very simple, the code has been written comments, see the effect:

Operation effect

Generate so file

In CMakeLists.txt, the compilation mode of library is set to SHARED mode. Clicking on the compilation button of AS, the so files corresponding to different CPU architectures will be generated in the app > build > intermediates > cmake > debug > obj directory:

so file directory

The generated so files can also be used in other projects. Create a jniLibs folder under the app > SRC > main directory of the project, copy the generated so files (with CPU architecture directory) into the jniLibs folder, and use them normally according to the call method mentioned above.

Configure abiFilters in app build.gradle to output so files for the specified ABI:

defaultConfig {
    ...
    
    ndk {
        abiFilters "armeabi", "armeabi-v7a", "arm64-v8a", "x86", "x86_64", "mips", "mips64"
    }
}

4. Write at the end

NDK Official Use Documents

More NDK development articles will be updated in the future, please look forward to it!

Posted by bleh on Thu, 16 May 2019 15:55:18 -0700