I. Development environment

Windows 10 AndroidStudio 3.1.2 NDK version: R16

2. Configure NDK environment variables:

As with Java JDK environment variables, you can configure NDK environment variables by yourself.

    

     

Create local methods in your own project:

Create native methods in Java classes.

The package name of the project or application is com.ang.ndkdemo

public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }} public String fromJNIString();}} public String fromJNIString(); }Copy the code

Create the.h header for the local fromJNIString() method

1, Enter javah -d d :\Demo\NDKDemo\app\ SRC \main\ jni-classpath in CMD or Terminal in AndroidStudio D:\Demo\NDKDemo\app\src\main\java com.ang.ndkdemo.MainActivity

javah -d D:\Demo\NDKDemo\app\src\main\jni -classpath D:\Demo\NDKDemo\app\src\main\java 
com.ang.ndkdemo.MainActivity
Copy the code
  • A, -d d :\Demo\NDKDemo\app\ SRC \main\jni Create the jni folder and specify the.h output directory
  • B, D:\Demo\NDKDemo\app\ SRC \main\jni The absolute path to the output of the.h header file to be created
  • C, D: \ Demo \ NDKDemo \ app \ SRC \ main \ Java com. Ang NDKDemo. MainActivity include local method (fromJNIString ()) the class path; Do not write D:\Demo\NDKDemo\app\ SRC \main\ Java \com\ang\ NDKDemo\ MainActivity (put the dot “in the package name. It was written with a slash “****”, which is not correct) **; * * com. Ang. Ndkdemo. MainActivity (note is a package name + class name).
  • Parameters that

-classpath: class search path, which means search from the current D:\Demo\NDKDemo\app\ SRC \main\ Java directory

-d: Puts the generated header file in the current JNI directory

-o: specifies the name of the generated header file. By default, the header file is generated using the class full path name (package name + class.h).

Note: -d and -o can use only one of the parameters.

Note: -d d :\Demo\NDKDemo\app\ SRC \main\jni and -classpath d :\Demo\NDKDemo\app\ SRC \main\ Java are interchangeable; And the following is equivalent to the above;

javah -classpath D:\Demo\NDKDemo\app\src\main\java -d D:\Demo\NDKDemo\app\src\main\jni com.ang.ndkdemo.MainActivity

Copy the code

            

Note: You can use -o to specify the name of the generated header file. If you do not specify the name of the generated header file, the generated header file is the class full path name (package name + class name.h) by default.

javah -classpath E:\Demo\JNIDemo\app\src\main\java -o E:\Demo\JNIDemo\app\src\main\java\jni\JNITest.h com.ang.MainActivity

Copy the code

2, after executing the above command: jni folder is created under the main folder of the project, and.h header file is automatically created under the jni folder. The header file name is also automatically generated and named com_ang_ndkDemo_mainActivity.h (package name + class name.h)

              

3, automatically generated com_ANG_NDkdemo_mainactivity. h header file code

/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_ang_ndkdemo_MainActivity */ #ifndef _Included_com_ang_ndkdemo_MainActivity #define _Included_com_ang_ndkdemo_MainActivity #ifdef __cplusplus extern "C" { #endif /* * Class: com_ang_ndkdemo_MainActivity * Method: fromJNIString * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_ang_ndkdemo_MainActivity_fromJNIString (JNIEnv *, jobject); /*JNIEnv* is the first parameter defining any native functions (including those registered by calling JNI's RegisterNatives function), and a pointer to the JVM function table. Each entry in the function table points to a JNI function, each of which is used to access specific data structures in the JVM. */ #ifdef __cplusplus } #endif #endifCopy the code

4, generated. H header file, if there is a “can’t find the class file” error please reference blog.csdn.net/ezconn/arti… This article

Note:

A. Package name or class name or method name containing an underscore _ should be connected with _1;

B. Overloaded local method names should be connected with double underscore __;

C. Change the slash (/) of parameter signature to underscore (_) and semicolon (;). Change the connection to “_2” and the left square bracket “[” to” _3 “;

In addition, for Java native methods, the difference between static and non-static methods is the second parameter, static is jclass, non-static is jobject; There are no modifiers in JNI functions.

Create c or C ++ files in jNI directory.

You can write the file name arbitrarily, but pay attention to the file type. Hello. C files (.c files are c files) represent c code; Hello. CPP (.cpp files are C++ files).

C++ code (note the difference between C and C++ code), the following are two implementations of C and C++ :

  • A, Hello. C file. There is no reference in C. Env is passed as a two-level pointer to a method called (*env) ->.
#include <jni.h>
#include "com_ang_ndkdemo_MainActivity.h"
 JNIEXPORT jstring JNICALL
 Java_com_ang_ndkdemo_MainActivity_fromJNIString(JNIEnv* env, jobject obj) {
     return (*env)->NewStringUTF(env,"I am From Native C");
 }
Copy the code
  • B, hello. CPP file. In C++ env is a pointer to the first level. Extern “C” is extern and extern “C” is extern and extern “C” is extern and extern “C” is extern and extern “C” is extern and extern “C” is extern Please keep my name and don’t give me the intermediate function name generated for the link; Exter “C”{jni code}.
#include <com_ang_ndkdemo_MainActivity.h>
#include <stdio.h>
 
JNIEXPORT jstring JNICALL
Java_com_ang_ndkdemo_MainActivity_fromJNIString(JNIEnv *env, jobject obj)
{
    return env->NewStringUTF("I am From Native C");
}
Copy the code

How do Java native methods link functions in C/C++? JNI can be registered statically and dynamically. This is done through static registration.

Static registration: Establish a one-to-one mapping between Java native methods and JNI functions based on function names.

Dynamic registration: Directly telling A Java Native method its pointer to the corresponding function in JNI.

Build. Gradle (Model: App)

The NDK {} is used to specify which abi libraries are to be compiled. If the NDK {} is not configured, the NDK will output the corresponding ABI libraries by default. It is best to configure, otherwise you will not be able to compile the ABI. So you need to adapt if your project already references other.

  

DefaultConfig {applicationId "com.ang.demo" minSdkVersion 19 targetSdkVersion 28 versionCode 1 versionName "1.0" TestInstrumentationRunner "android. Support. The test. The runner. AndroidJUnitRunner" / / the NDK compiler generated. So file the NDK {moduleName "Java2c." // The name of the generated so is specified in the android. mk file. We can not write abiFilters "armeabi", "armeabi-v7a", "x86". }}Copy the code

7. Write the Android.mk file

The Android.mk file generally contains the following information, which is almost a template; So library name and C or C++ file name can be modified to use;

LOCAL_PATH:= $(call my-dir)# include $(CLEAR_VARS)# Include LOCAL_MODULE:= hello # LOCAL_SRC_FILES:= hello. #include $(BUILD_SHARED_LIBRARY)# include $(BUILD_STATIC_LIBRARY) #include $(BUILD_SHARED_LIBRARY)# include $(BUILD_STATIC_LIBRARY) #include $(BUILD_SHARED_LIBRARYCopy the code

Another option is to have it generated automatically by androidstudio; Here’s how I got the auto-generated Android.mk file:

A, immediately after step 6, click on the Androidstudio menu bar Build ->ReBuildProject

Error:

        

B, an Android.mk file is automatically created in the app — > Build — >intermediater — > NDK (automatic creation) directory

         

The android. mk file is as follows:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
 
LOCAL_MODULE := Java2c
LOCAL_LDFLAGS := -Wl,--build-id
LOCAL_SRC_FILES := \
	D:\Demo\NDKDemo\app\src\main\jni\Hello.cpp \
 
LOCAL_C_INCLUDES += D:\Demo\NDKDemo\app\src\debug\jni
LOCAL_C_INCLUDES += D:\Demo\NDKDemo\app\src\main\jni
 
include $(BUILD_SHARED_LIBRARY)
Copy the code

Modify the default compiler

Right-click the Project and click Link C++ Project with Gradle to modify the Androidstudio default build tool. In BuildSystem, select NDK — build. In ProjectPath, Build. Gradle (Model: ExternalNativeBuild {ndkBuild {path ‘SRC /main/jni/ android.mk ‘}}

// the Link C++ Project with Gradle option is no longer displayed when you right-click the Project. externalNativeBuild { ndkBuild { path 'src/main/jni/Android.mk' } }Copy the code

Note: Sometimes you need to display the Link C++ Project with Gradle option again, Delete externalNativeBuild {ndkBuild {path ‘SRC /main/jni/ android. mk’}} Click sync now; Right-click again and the project appears;

** 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 runs CMake or NdK-build as a dependency and packages the shared.so library into APK. ExternalNativeBuild is the configured script path;

Load the dynamic library in MainActivity:

Note: the filename specified for loading the generated dynamic library (system.loadlibrary (“Java2c”);) And the name specified when generating. So (NDK {moduleName “Java2c”} in buil.gradle), and LOCAL_MODULE := Java2c in Android.mk.

Such as: The file named Java2JNI for loading the generated dynamic library and the file named Java2JNI for generating.so above: If Java2c and LOCAL_MODULE := in Android.mk are inconsistent, the UnsatisfiedLinkError exception is generated.

public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toast.makeText(this,new Java2C().fromJNIString(),Toast.LENGTH_LONG).show(); Static {system.loadLibrary ("Java2c"); } public native String fromJNIString(); }Copy the code

About UnsatisfiedLinkError abnormal reasons, there are many here on the NDK development summary of several possible reasons: https://blog.csdn.net/ezconn/article/details/82531893

The. So library should be placed in the specified path; If you need the. So library (for other projects, such as baidu Map service, you need to use the provided. So library) compile and use the. So library manually as follows;

                                         Manually compile the. So file

The second way, starting from Step 8, is to manually generate the. So library without specifying the AndroidStudio build tool (Cmake or nk-build)

A, CMD or Android Studio Terminal access the jNI upper directory

      

B. Input the ndk-build command to generate a libs folder in the directory of the same level as JNI, in which. So files corresponding to each CUP architecture are generated.

      

C, application. So dynamic library

  • If you do not change the location of the manually generated. So library, you need to configure it in build.gradle(model. app), because the default location of the. So library is SRC /main/jniLibs
SourceSets {main {jnilibs. srcDirs = [' SRC /main/libs']}}Copy the code
  • SRC /main/jniLibs is the default path to the library file. This eliminates the need to configure sourceSets in build.gradle(Model:app);

              

  • • Build. Gradle (model.app) • build. Gradle (model.app) • build. Gradle (model.app) • build. Gradle (model.app) • build
    sourceSets {
        main {
           jniLibs.srcDirs = ['libs']
        }
    }
Copy the code

D, note: this manual generation. So library method, using NDK17 generation failure, above is the application of NDK16, because NDK17 no longer supports MIPS, Armeabi CPU architecture,

Other related

There are three main ways to compile code using NDK:

1. Ndk-build based on Make.

2. CMake.

3. Standalone toolchain for integration with other compilation systems or with Configure-based projects.

If it will help you

Might as well add a concern, a point of praise ha, your every small move is a great support to me!