Android JNI learning - "hello world" of actual JNI

This series of articles is as follows:

The local content is mainly introduced as follows:

  • 1. Environmental display
  • 2. Specific process of traditional mode
  • 3. Problems related to traditional methods
  • 4. Traditional so file
  • 5. Demonstrate the process through the CMake tool demo
  • 6. Behind the CMake tool demo
  • 7. Application of CMake
  • 8. Compile using the experimental plugin plug-in

Outline of this article.png

1, Environmental display

The operating system is

Operating system.png

The Android environment is:

Android environment.png

NDK environment

NDK environment.png

Simulator is

Simulator.png

2, Specific process of traditional mode

The specific process is as follows:

(1) Create project

First, create an Android project in Android Studio. The package name is gebilaolitou.ndkdemo

(2) Create a tool class that references a local library

Then create a class named NDKTools

The code is as follows:

package gebilaolitou.ndkdemo;

public class NDKTools {

    public static native String getStringFromNDK();

}

(3) Modify relevant UI display

Add id to textview in xml corresponding to MainActivity
As follows:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="gebilaolitou.ndkdemo.MainActivity">

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>

Then modify MainActivity and call getStringFromNDK() method of NDKTools in it.

package gebilaolitou.ndkdemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        String text = NDKTools.getStringFromNDK();
        Log.i("gebilaolitou","text="+text);
        ((TextView)findViewById(R.id.tv)).setText(text);
    }
}

(4) Get classes file

In Android Studio, click Make Project or Rebuild Project in Build to compile to obtain the intermediate file. As shown below

Compile.png


After compiling, we can get the class file, as shown in the figure below

classes file.png

(5) Enter the corresponding directory

Click Terminal under Android Studio, then jump to NDKDemo/app/build/intermediates/classes/debug (where NDKDemo is the root directory of the project), and execute pwd confirmation directory in Terminal.

(6) Get. h file

Under NDKDemo/app/build/intermediates/classes/debug, execute the following command javah -jni gebilaolitou.ndkdemo.NDKTools. If there are no problems, gebilaolitou will be generated under NDKDemo/app/build/intermediates/classes/debug_ ndkdemo_ Ndktools. H file. As shown below

Header file.png

The contents are as follows:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class gebilaolitou_ndkdemo_NDKTools */

#ifndef _Included_gebilaolitou_ndkdemo_NDKTools
#define _Included_gebilaolitou_ndkdemo_NDKTools
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     gebilaolitou_ndkdemo_NDKTools
 * Method:    getStringFromNDK
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_gebilaolitou_ndkdemo_NDKTools_getStringFromNDK
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

As shown below

Header file content.png

(7) Add corresponding. c file

Create a directory named jni under the main directory of the project, and then cut the. h file just now. Create a new c file in the jni directory. Name it ndkdemotest.c. The project directory is as follows:

jnipng

(8) Write ndkdemotest.c file

ndkdemotest.c will be negotiated as follows

#include "gebilaolitou_ndkdemo_NDKTools.h"

JNIEXPORT jstring JNICALL Java_gebilaolitou_ndkdemo_NDKTools_getStringFromNDK
  (JNIEnv *env, jobject obj){
     return (*env)->NewStringUTF(env,"Hellow World,This is Lao Li Tou's next door NDK First line of code");
  }

ndkdemotest.png

There is not much content. There are two parts. The first part is to add gebilaolitou_ndkdemo_NDKTools.h header file, and then the concrete implementation of Java_gebilaolitou_ndkdemo_NDKTools_getStringFromNDK function

(9) Add and write the Android.mk file

Similarly, add an Android.mk file in the jni directory, and its directory structure is as follows:

Add Android.mk file.png

Also write the following in the Android.mk file

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := ndkdemotest-jni

LOCAL_SRC_FILES := ndkdemotest.c

include $(BUILD_SHARED_LIBRARY)

Android.mk content.png

About the Android.mk language, a separate article will be written later to explain the content of the above code

  • LOCAL_ Path: = $(call my DIR): each Android.mk file must start with a definition. It is used to find source files in the development tree. The macro my dir is provided by Build System. Returns the path containing the Android.mk directory.
  • include $(CLEAR_VARS) : CLEAR_ Vars variables are provided by Build System. And point to a specified GNU Makefile, which is responsible for cleaning up many local files_ xxx. For example, LOCAL_MODULE,LOCAL_SRC_FILES,LOCAL_STATIC_LIBRARIES, etc. But not clean up LOCAL_PATH. This cleanup is necessary because all compilation control files are parsed and executed by the same GNU Make, and their variables are global. Therefore, after cleaning, they can affect each other.
  • LOCAL_MODULE := ndkdemotest-jni: LOCAL_MODULE modules must be defined to represent each module in Android.mk. The name must be unique and contain no spaces. Build System will automatically add appropriate prefixes and suffixes. For example, to generate a dynamic library, generate libdemo.so. Note that if the module name is defined as libabd, libabc.so is generated. No more prefixes.
  • LOCAL_ SRC_ Files: = ndkdemotest. C: this line of code represents the C/C + + source code to be packaged. There is no need to list the header files. build System will automatically help us find the dependent files. The default C + + source code extension is. cpp.
  • include $(BUILD_SHARED_LIBRARY): BUILD_ SHARED_ Library is a variable provided by Build System and points to a GUN Makefile Script. It is responsible for collecting all local data since the last call to include $(CLEAR_VARS)_ xxxxinx. And decide what type to compile
    • BUILD_STATIC_LIBRARY: compile to static library
    • BUILD_SHARED_LIBRARY: compile as a dynamic library
    • BUILD_EXECUTABLE: compiled as a Native C executable
    • BUILD_ Prebuild: This module has been precompiled

PS: if Android.mk is not written here, the following questions will be prompted:

Error:Execution failed for task ':app:compileDebugNdk'.
> Error: Flag android.useDeprecatedNdk is no longer supported and will be removed in the next version of Android Studio.  Please switch to a supported build system.
  Consider using CMake or ndk-build integration. For more information, go to:
   https://d.android.com/r/studio-ui/add-native-code.html#ndkCompile
   To get started, you can use the sample ndk-build script the Android
   plugin generated for you at:
   /Users/gebilaolitou/AndroidStudioProjects/JNIDemo/app/build/intermediates/ndk/debug/Android.mk
  Alternatively, you can use the experimental plugin:
   https://developer.android.com/r/tools/experimental-plugin.html
  To continue using the deprecated NDK compile for another 60 days, set 
  android.deprecatedNdkCompileLease=1523001628930 in gradle.properties

It is all in English. The simple translation is as follows:

Error: failed to execute app:compileDebugNdk task
Error: the android.usedeprecated NDK flag is no longer supported and will be removed in future versions of Android Studio. Please switch to CMake build system or integrate in NDK build. For more information, please refer to https://d.android.com/r/studio-ui/add-native-code.html#ndkCompile . You can use the Android sample NDK build script to generate plug-ins at the following locations:
/Users/gebilaolitou/AndroidStudioProjects/JNIDemo/app/build/intermediates/ndk/debug/Android.mk. In addition, you can also use experimental plug-ins https://developer.android.com/r/tools/experimental-plugin.html
If you want to continue compiling with the deprecated NDK for 60 days, you need to set Android. Deprecated ndkcompilelease = 1523001628930 in gradle.properties

For the above reasons, I need to set Android.mk

(10) Modify the corresponding configuration file

First, check whether there is an NDK path in the local.properties file. If there is no NDK path, add an NDK path. For example, mine is as follows:

ndk.dir=/Users/debilaolitouLibrary/Android/sdk/ndk-bundle
sdk.dir=/Users/debilaolitouLibrary/Library/Android/sdk

Secondly, modify the contents in build.gradle under the app module directory, as follows:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "gebilaolitou.ndkdemo"
        minSdkVersion 19
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

        ndk{

            moduleName "ndkdemotest-jni"
            abiFilters "armeabi", "armeabi-v7a", "x86"

        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        externalNativeBuild {
            ndkBuild {
                path 'src/main/jni/Android.mk'
            }
        }
        sourceSets.main {
            jni.srcDirs = []
            jniLibs.srcDirs = ['src/main/jniLibs']
        }
    }
}

So you have the so file (no so file has been generated at this time)

(11) Modify reference class

Finally, add static initialization code in NDKTools class, as follows:

public class NDKTools {

    static {
        System.loadLibrary("ndkdemotest-jni");
    }

    public static native String getStringFromNDK();
}

Finally, run as shown below

Show. png

3, Problems related to traditional methods

Some students will report the following errors when running:

Error:Execution failed for task ':app:compileDebugNdk'.
> Error: Your project contains C++ files but it is not using a supported native build system.
  Consider using CMake or ndk-build integration. For more information, go to:
   https://d.android.com/r/studio-ui/add-native-code.html
  Alternatively, you can use the experimental plugin:
   https://developer.android.com/r/tools/experimental-plugin.html

First, add a sentence after checking the gradle.properties file in your project

Android.useDeprecatedNdk=true 

4, Traditional so file

You may have questions. Where is the so? When we usually use the so of the third-party sdk, we have to paste and copy it into the project. However, the whole process described above does not appear the. So file. Where is the. So?

In fact, Android Studio automatically helps us put so into apk. If we want to find it, we can find it, as shown below:

Location of so file.png

The above method is the traditional Android Studio mode. Is there a simpler method? Yes, let's continue to look at it

5, Demonstrate the process through the CMake tool demo

(1) First, make sure you have CMake locally. Let's take a look at SDK Tools

SDK Tools.png

I see the third CMake above. I don't have it locally, so I want to install it

(2) Check the Include C++ Support check box.

In the Configure your new project section of the wizard, select the Include C++ Support check box.
As shown below

Check. png

There is a pit here. Many students say that I don't have the Include C++ Support check box. This is because of the "bug" designed by Android Studio. If you enlarge this dialog box, it will appear. Because ordinary Android projects can't use it, if you don't deliberately enlarge it, you will selectively "hide" it. It's too JB pit.

Then continue to the next step until the Customize C++ Support section

(3) Customize custom items for C + + support

As follows:

Mode.png

There are three projects in it

  • C++ Standard: that is, C++ Standard. Use the drop-down list to select the C++ Standard you want to use, and select Toolchain Default to use the default CMake setting.
  • Exceptions Support: select this check box if you want to enable support for C + + exception handling. If you enable this check box, Android Studio will add the - fexceptions flag to the cppFlags of the module level build.gradle file, and Gradle will pass it to CMake.
  • Runtime Type Information Support: select this check box if the developer wants to support RTTI. If this check box is enabled, Android Studio will add the - frtti flag to the cppFlags of the module level build.gradle file, and Gradle will pass it to CMake.

Finally, click Finish.

(4) Check Android directory

After Android Studio completes the creation of a new Project, open the Project mine mouth from the left side of the IDE and select the Android view. As shown in the following figure, Android Studio will add cpp and External Build Files groups:

Android mode.png

The figure shows the Android view group of the developer's original source file and external build script.

PS: (this view cannot reflect the actual file hierarchy on disk, but groups similar files into a group to simplify Project navigation). If it is Project mode, it is as follows:

Project mode.png

Let's briefly introduce the two extra folders:

  • In CPP folder: you can find all the original source files and other construction libraries belonging to the project. For the new project, Android Studio will create a sample C + + source file, native-lib.cpp, and place it in the src/main/cpp / directory of the application module. This sample code provides a simple C + + function stringFromJNI(), which can return the string "Hello from C + +"
  • In the External Build Files folder: you can find the build script of CMake or NDK build. Just as the build.gradle file instructs Gradle to build applications, CMake and NDK build need a build script to understand how to build native libraries. For new projects, Android Studio will create a CMake build script CMakeLists.txt and place it in the module root directory.

(5) Direct operation project

Let's run the project directly and see the results

Result 1.png

(6) Modify native-lib.cpp

At this time, we modify native-lib.cpp. The content of native-lib.cpp is as follows:

Native lib.cpp content.png

run the project directly and see the results. As follows:

Result 2.png

We see that the corresponding text has been modified

6, Behind the CMake tool demo

We saw it. We didn't do anything, but we automatically realized the implementation of C + +. What's the principle behind it? Let's just think about it?

(1) Entrance to CMake

Since it can run, there must be an entrance. Where is the entrance?

~~~~~~~~~~~~~~~~~~~~~~~~~~Separator~~~~~~~~~~~~~~~~~~~~

Let me tell you how I imagined it. First, when we click the run button in Android Studio, it executes Gradle for packaging. Therefore, there must be a corresponding entry in the build.gradle of the project about how CMake is implanted.

What can we draw inferences from the above ideas? Yes, similar operations are generally implemented in build.gradle, because Android Studio is implemented through Gradle at present

Let's take a look at the code in build.gradle, as follows:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "gebilaolitou.cmakendkdemo"
        minSdkVersion 23
        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 {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}

It is similar to the projects we usually build, that is, an additional piece of content, external native build. Let's focus on external native build

(2) externalNativeBuild

We can see in build.gradle that externalNativeBuild is used in two places, one inside defaultConfig and the other outside defaultConfig.

  • cmake in externalNativeBuild outside defaultConfig indicates the path of CMakeList.txt (in this project, it is in the same directory as build.gradle).
  • Cmake in externalNativeBuild in defaultConfig is mainly filled with the command parameters of cmake. That is, the parameters in arguments are finally converted into an executable cmake command, which can be

externalNativeBuild - cmake outside defaultConfig indicates the path of CMakeList.txt;
externalNativeBuild - cmake in defaultConfig is mainly used to fill in the command parameters of CMake. That is, the parameters in arguments are finally converted into an executable CMake command, which can be found in APP / externalnativebuild / CMake / debug / {ABI} / CMake_ build_ Found in command.txt. as follows
The path location is shown in the figure below:

Path.png

The contents are as follows:

arguments : 
-H/Users/gebilaolitou/Desktop/codeLib/CMakeNDKDemo/app
-B/Users/gebilaolitou/Desktop/codeLib/CMakeNDKDemo/app/.externalNativeBuild/cmake/debug/x86
-DANDROID_ABI=x86
-DANDROID_PLATFORM=android-23
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=/Users/gebilaolitou/Desktop/codeLib/CMakeNDKDemo/app/build/intermediates/cmake/debug/obj/x86
-DCMAKE_BUILD_TYPE=Debug
-DANDROID_NDK=/Users/gebilaolitou/Library/Android/sdk/ndk-bundle
-DCMAKE_CXX_FLAGS=
-DCMAKE_TOOLCHAIN_FILE=/Users/gebilaolitou/Library/Android/sdk/ndk-bundle/build/cmake/android.toolchain.cmake
-DCMAKE_MAKE_PROGRAM=/Users/gebilaolitou/Library/Android/sdk/cmake/3.6.4111459/bin/ninja
-GAndroid Gradle - Ninja
jvmArgs : 

For more command parameters and meanings that can be filled in, see Android NDK cmake documentation

ok, since CMakeLists.txt is mentioned above, let's take a look at CMakeLists.txt

(3) CMakeLists.txt

CMakeLists.txt mainly defines which files need to be compiled and the relationship with other libraries. Let's take a look at the content of CMakeLists.txt in our project

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
             native-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/cpp/native-lib.cpp )

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
              log-lib

              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

Many of the above are comments. Let's remove the comments to a "concise and capable version" as follows:

cmake_minimum_required(VERSION 3.4.1)

add_library( # Sets the name of the library.
             native-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/cpp/native-lib.cpp )


find_library( # Sets the name of the path variable.
              log-lib

              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )

target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

CMakeLists.txt we can see that it is mainly divided into four parts. Let's take a look in turn

  • cmake_minimum_required(VERSION 3.4.1): Specifies the minimum version of CMake
  • add_library: create a static or dynamic library and provide its associated source file path. Developers can define multiple libraries, and CMake will automatically build them. Gradle can automatically package them into APK.
    • The first parameter - native lib: is the name of the library
    • The second parameter - SHARED: is it the category of the library, dynamic or static
    • The third parameter -- Src / main / CPP / native lib.cpp: is the path to the source file of the library
  • find_library: find a precompiled library and save it as a variable. Because CMake will include the system library when searching the library path, and CMake will check the name of its previously compiled library, developers need to ensure the uniqueness of the name of the library added by developers.
    • The first parameter - log lib: sets the name of the path variable
    • The first parameter - log: Specifies the name of the NDK library so that CMake can find the library
  • target_link_libraries: Specifies that CMake links to the target library. Developers can link multiple libraries. For example, developers can define library construction scripts here and precompile third-party libraries or system libraries.
    • The first parameter - native lib: the specified target library
    • The first parameter - ${log lib}: link the target library to the log Library in the NDK,

This is actually the most basic CMakeLists.txt. In fact, CMakeLists.txt can be very powerful, such as custom commands, finding files, including header files, setting variables, etc. CMake is recommended here Official website document , but it's in English. It's not easy to read. You can refer to Chinese CMake manual

After the above analysis, CMakeLists.txt, we can roughly know the overall construction process of CMake. Let's take a look

(4) Operation process of CMake

  • 1. Gradle calls the external build script CMakeLists.txt
  • 2. CMake compiles the C + + source file native-lib.cpp into the shared object library according to the command of the build script, and names it libnative-lib.so. Gradle will then package it into APK
  • 3. When running, the MainActivity of the application will use System.loadLibrary() to load the native library. The application can use the library's native function stringFromJNI().

PS: one thing to note here is that Instant Run is incompatible with native projects

If you want to see whether Gradle packages the native libraries into APK, you can use Analyze APK to detect.

7, Application of CMake

When making daily requirements, we often encounter a problem, that is, adding C library to existing projects, so we can't use CMake through the above creation process. What can we do?

In fact, it doesn't matter. CMake also provides such functions. Now let's go back to the first demo above and delete all codes related to NDK. After deletion, its directory is as follows:

New directory.png

(1) Create source file

That is, create a new directory under the main directory, and we'll call it cpp. Then create a C + + source file under this directory (right-click the directory you just created and select new > C / C + + source file). We named it native lib.

After creation, the directory is as follows:

Create source file.png

(2) Create CMake build script

At present, there is no CMake build script in this project, so we need to create one ourselves and include the appropriate CMake command. The CMake build script is a plain text file, and the name must be CMakeLists.txt

To create a plain text file that can be used as a CMake build script, follow these steps:

  • 1. Open the Project pane from the left side of Android Studio and select Project view from the drop-down menu.
  • 2. Right click the root directory of the module and select New - > file.
    PS: this location is not fixed. You can choose any location, but you need to write this location into the build script when configuring the build script
  • 3. Enter CMakeLists.txt as the file and click OK

After creation, the directory is as follows:

CMakeLists.txt.png

(3) Write data to CMake script file

After the above explanation, I won't explain it in detail. The contents are as follows:

cmake_minimum_required(VERSION 3.4.1)

add_library( # Sets the name of the library. 
             native-lib
             # Sets the library as a shared library.
             SHARED
             # Provides a relative path to your source file(s). 
             src/main/cpp/native-lib.cpp )

find_library( # Defines the name of the path variable that stores the
              # location of the NDK library.
              log-lib

              # Specifies the name of the NDK library that
              # CMake needs to locate.
              log )
              

target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the log library to the target library.
                       ${log-lib} )

(4) Associate Gradle to a native library

To associate gradle with the native library, you need to provide a path to the CMake or NDK build script file. When building an application, gradle will run CMake or NDK build as a dependency and package the shared library into APK. Gradle also uses build scripts to learn about adding those files to Android projects.
If the native file does not have a build script, you need to create a CMake build script

There are two ways to associate with the native library: one is through Android Studio and the other is manual. In fact, the things behind it are the same. Let's explain it

1. Implemented via Android Studio

  • 1. Open the Project pane from the left side of the IDE and select the Android view
  • 2. Right click the module you want to associate with the native library (here is the app module), and select Link C++ Project with Gradle from the menu. As shown below
  • 3. Select CMake from the drop-down menu. Use Project Pat to specify the just '` CMakeLists.txt' script file for the external CMake project
  • 4. Click OK.

Link C++ Project with Gradle.png

2. Manual implementation

To manually configure Gradle to associate to the native library, you need to add the externalNativeBuild {} block to the module level build.gradle file and configure it with cmake {}

The code is as follows:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "gebilaolitou.ndkdemo"
        minSdkVersion 19
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

    }

    externalNativeBuild {
        cmake {
            path 'CMakeLists.txt'
        }
    }
}

(5) Write native-lib.cpp

This is very simple, as follows:

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

extern "C"
JNIEXPORT jstring

JNICALL
Java_gebilaolitou_ndkdemo_NDKTools_getStringFromNDK(
        JNIEnv *env, jobject /* this */) {
    std::string hello = "(*^__^*) Hee hee~Hello from C++ Old Li Tou next door";
    return env->NewStringUTF(hello.c_str());
}

Then add a reference to NDKTools.java as follows:

package gebilaolitou.ndkdemo;


public class NDKTools {

    static {
        System.loadLibrary("native-lib");
    }

    public static native String getStringFromNDK();
}

Then run it directly, and the results are as follows:

Result 3.png

8, Introduction to using the empirical plugin plug-in

We have a troublesome thing in using NDK development, that is, to write Android.mk and Application.mk, and the Android Studio plug-in gradle empirical is used to solve this problem. Therefore, the gradle experimental plug-in can be used for NDK development without writing. MK files.

Gradle experimental is an experimental project of Android Studio. It is a plug-in based on gradle. It is mainly used to automate the configuration and implementation of NDK. There is no need to write Android.mk and Android.mk. It is also more friendly for debugging NDK projects, but it is not supported now. Please see for details Experimental Plugin User Guide

Note to experimental Android plugin users: The experimental plugin will no longer be supported after version 0.11.0 (released October 25, 2017). That's because the experimental plugin is designed around a Software Component Model that Gradle announced they will no longer support (read their blog post here). Gradle has backported many features from the component model, which are now available with Android plugin 3.0.0, such as variant-aware dependency resolution, and api and implementation dependency configurations. Gradle is working on backporting built-in support for compiling C/C++ code, and the Android plugin will integrate that support when it becomes available. Until then, you can either keep using experimental plugin 0.11.0 with Android Studio 3.0 or later, or migrate to Android Studio's support for using external native build tools.

The following is a simple translation:

For users who use the experiential Android plug-in, please note that we will no longer support the experiential plug-in after 0.11.0 released on October 25, 2017. Because gradle no longer supports the design of empirical plug-ins based on the software component model (through their blog). In version 3.0.0 of Gradle Android plug-in, many functions in the build model are now supported. For example, variant aware dependency resolution and api and implementation dependency configurations. Gradle now supports built-in support for compiling C/C + + code and integrates this support when Android plug-ins are available again. In between, you can continue to use the experimental plug-in of Android Studio 3.0 or later, or use the external native build tools supported by Android Studio.

Last article Android JNI (I) -- Fundamentals of NDK and JNI
Next article Android JNI learning (III) -- mutual call between Java and Native



Author: Lao Li Tou next door
Link: https://www.jianshu.com/p/b4431ac22ec2
Source: Jianshu
The copyright of Jianshu belongs to the author. Please contact the author for authorization and indicate the source for any form of reprint.

Tags: Java Android Android Studio JNI

Posted on Wed, 17 Nov 2021 20:58:21 -0500 by f8ball