SoFunction
Updated on 2025-04-06

Android NDK development detailed introduction

Android NDK development

1. Background of NDK generation

Since its birth, the Android platform has supported C and C++ development. As we all know, Android's SDK is based on Java implementation, which means that third-party applications developed based on Android SDK must use the Java language. But this does not mean that "third-party applications can only use Java". When the Android SDK was first released, Google claimed that its virtual machine Dalvik supports JNI programming, that is, third-party applications can call their own C dynamic library through JNI, that is, on the Android platform, the "Java+C" programming method can always be implemented.

However, Google also stated that programming with native SDK also has some disadvantages compared to Dalvik virtual machines, and no JNI help can be found in the Android SDK documentation. Even if third-party application developers use JNI to complete their own C dynamic link library (so) development, how can so be packaged into apk and published with the application? There are also technical obstacles here. For example, the program is more complex, compatibility is difficult, the Framework API cannot be accessed, and the Debug is more difficult. Developers need to use it themselves.

So NDK came into being. The full name of NDK is Native Development Kit.

The release of NDK has finally turned the development method of "Java+C" and has become an official supported development method. NDK will be the beginning of the Android platform supporting C development.

2. Why use NDK

1. Code protection. Since the java layer code of apk is easily decompiled, the C/C++ library is more difficult to reverse the exchange.

2. It is easy to use the existing open source library. Most of the existing open source libraries are written in C/C++ code.

3. Improve the execution efficiency of the program. High-performance application logic will be developed using C, thereby improving the execution efficiency of the application.

4. Easy to transplant. The C/C++ library can be easily used again on other embedded platforms.

3. Introduction to NDK

It is a collection of tools

NDK provides a series of tools to help developers quickly develop dynamic libraries of C (or C++) and can automatically package So and Java applications into apk together. These tools are of great help to developers.

NDK integrates a cross compiler and provides corresponding mk file isolation of CPU, platform, ABI and other differences. Developers only need to simply modify the mk file (pointing "what files need to be compiled", "compilation feature requirements", etc.) to create so.

NDK can automatically package so and Java applications together, greatly reducing the packaging work of developers.

Provides a stable, limited-function API header file statement

Google explicitly states that the API is stable and supports the currently released API in all subsequent versions. As can be seen from this version of NDK, these APIs support very limited functions, including: C standard library (libc), standard mathematics library (libm), compression library (libz), and Log library (liblog).

4. Construction of NDK development environment

1. Download and install Android NDK

address:/sdk/ndk/

2. Download and install cygwin

Since NDK must use make and gcc when compiling code, you must first build a linux environment. cygwin is a Unix simulation environment running on the Windows platform. It is very useful for learning Unix/linux operating environments, or porting applications from Unix to Windows. Through it, you can use NDK to compile C and C++ code without installing linux. Download address:

1) Then double-click Run, and after running, you will see the installation wizard interface.

2) Click Next, and you will choose the installation method:

Install from Internet: Download directly from the Internet and install immediately (after the installation is completed, the downloaded installation file will not be deleted, but will still be retained for installation next time).
Download Without Installing: Just download the installation file locally, but not install it for the time being.
Install from Local Directory: Do not download the installation file, install it directly from a directory containing the installation file in the local area.

3) Select the first item and click Next.

4) Select the directory to be installed. Note that it is best not to put it in a directory with Chinese and spaces, as it seems to cause installation problems. Other options do not need to be changed. Click Next:

5) The previous step is to select the directory where cygwin is installed. This is to select the directory where the installation package you downloaded is located. The default is the directory you run. Just click Next:

6) At this time, you have three connection methods:

Direct Connection: Direct connection.
Use IE5 Settings: Use IE's connection parameter settings to connect.
Use HTTP/FTP Proxy: Use HTTP or FTP proxy server to connect (you need to enter the server address and port number).

Users can make choices based on the actual situation of their network connection. Generally, under normal circumstances, they choose the first type, that is, the direct connection method. Then click "Next".

7) This is to select the site you want to download, and then click Next.

8) The installation package list will be downloaded at this time

9) Search can enter the name of the package you want to download, and can quickly filter out the package you want to download. The four radio buttons are the style of the tree below, and the default is OK, and there is no need to move. The default view is Category. It is recommended to change it to full to show all packages and check it again. Some packages saved are hidden. The check box in the lower left corner is whether to hide the expired package. It is checked by default. Just ignore it. Let’s start downloading the package we want to install. In order to avoid downloading all the packages, here are the packages that can be used to develop NDK later: autoconf2.1, automake1.10, binutils, gcc-core, gcc-g++, gcc4-core, gcc4-g++, gdb, pcre, pcre-devel, gawk, make 12 packages.

10) Then start to choose to install these packages, click skip, and turn it into a digital version format. To ensure that the Bin item becomes a cross, and the Src item is the source code, there is no need to choose this.

11) Let’s test whether cygwin is already installed.

Run cygwin and enter the: cygcheck -c cygwin command in the pop-up command window. It will print out the current version and running status of cygwin. If status is OK, cygwin will run normally.

Then enter gcc –version, g++ --version, make –version, and gdb –version in turn for testing. If the version information and some description information are printed out, the cygwin installation will be successful!

3. Configure NDK environment variables

a. First, find the installation directory of cygwin, and find a home\<your username>\.bash_profile file. Mine is: E:\cygwin\home\Administrator\.bash_profile. (Note: When I installed, there was nothing under my home folder. The solution is: First, open the environment variable, delete the HOME variable in the user variable inside, create a folder named Administrator (it is the username) under the E:\cygwin\home folder, and then copy E:\cygwin\etc\skel\.bash_profile to this folder).

b. Open the bash_profile file and add NDK=/cygdrive/<your drive letter>/<android ndk directory> For example:

NDK=/cygdrive/e/android-ndk-r5

export NDK

The name NDK is chosen casually. For the sake of convenience in the future, choose a short name and save it.

c. Open cygwin and enter cd $NDK. If you output the /cygdrive/e/android-ndk-r5 information configured above, it means that the environment variable is set successfully.

4. Use NDK to compile the program

a. Now let's use the installed NDK to compile a simple program. Let's choose the example that comes with ndk hello-jni. My is located in E:\android-ndk-r5\samples\hello-jni (depending on your specific installation location).

b. Run cygwin, enter the command cd /cygdrive/e/android-ndk-r5/samples/hello-jni, and enter the E:\android-ndk-r5\samples\hello-jni directory.

c. Enter $NDK/ndk-build. After successful execution, it will automatically generate a libs directory and place the compiled .so file inside. ($NDK is the environment variable we configured earlier, and ndk-build is the compiler that calls ndk)

d. At this time, go to the hello-jni libs directory to see if there is any generated .so file. If so, your ndk will run normally!

5. Integrate the c/c++ development environment in eclipse

a. Install Eclipse's C/C++ environment plug-in: CDT, select online installation here. Log in first/cdt/, find the online installation address corresponding to your Eclipse version of CDT plug-in.

b. Then click the Help menu and find the Install New Software menu

c. Click the Add button, fill in the address you selected, and after coming out of the plug-in list, select Select All, and then select Next to complete the installation.

d. After the installation is completed, right-click to create a new project in eclispe. If a c/c++ project appears, it means that your CDT plug-in has been installed successfully!

6. Configure the compiler for C/C++

a. Open eclipse, import the hello-jni example that comes with ndk, right-click the project name, click Properties, and pop up the configuration interface, then click Builders to pop up the project's compilation tool list, then click New to add a new compiler. After clicking, the addition interface appears, select Program, and click OK.

b. The addition interface appears. First, give the compiler a name, such as: C_Builder, set Location to <your cygwin installation path>\bin\ program, example: E:\cygwin\bin\, set Working Directory to <your cygwin installation path>\bin directory, for example: E:\cygwin\bin, set Arguments to --login -c "cd /cygdrive/e/android-ndk-r5/samples/hello-jni && $NDK /ndk-build"

In the above configuration, /cygdrive/e/android-ndk-r5/samples/hello-jni is the directory of the program you are currently trying to compile. $NDK is the environment variable of ndk that was previously configured. These two are configured according to your specific installation directory. The others do not need to be changed. Arguments's parameters are actually passed parameters to the command line program, enter the program directory to be compiled, and then run the ndk-build compiler.

c. Then switch to the Refresh tab and hook the Refresh resources upon completion

d. Then switch to the Build Options tab and check the last three items

e. Then click the Specify Resources button, select the resource directory, and check your project directory.

f. Finally, click Finish, click OK to save the configuration just now. Note: If the compiler you configured is under other compilers, remember to click the Up button to rank it first. Otherwise, the compilation of C code is later than that of Java code, which will cause your C code to be compiled twice before you can see the latest modifications.

g. The compilation configuration has also been configured. Now let’s test whether it can be compiled automatically. Open the file in the project jni directory and change the prompt Hello from JNI! to other text: such as: Hello, My name is alex., and then run your program in the simulator. If your latest modified text is displayed in the simulator, then Congratulations! You have all configured successfully!

5. Develop your own NDK program

The best way to get started is to learn the examples that come with Android. Here we can achieve this by learning the demo program, hello-jni, which comes with Android's NDK.

1. Construction of the development environment

1) Android NDK development needs to be carried out under Linux: Because the code written in C/C++ needs to be generated to generate .so files that can run on arm, this requires a cross-compilation environment, and cross-compilation needs to be completed under the Linux system.

2) Install the Android-ndk development package. This development package can be downloaded on Google android official website: Only through this development package tool can you compile the C/C++ code of Android jni into a library

3) Android application development environment: including eclipse, java, android sdk, adt, etc.

I won’t talk about how to download and install android-ndk here. After installation, you need to add the path force of android-ndk to the environment variable PATH:

sudo gedit /etc/environment

Add your Android-ndk installation power to the PATH environment variable in the environment, and then let the changed environment variable take effect immediately:

source  /etc/environment

After the above steps, click on the command line:

ndk-bulid

The following error pops up, instead of saying ndk-build not found, it means that the ndk environment has been installed successfully.

Android NDK: Could not find application project directory !   
Android NDK: Please define the NDK_PROJECT_PATH variable to point to it.   
/home/braincol/workspace/android/android-ndk-r5/build/core/:85: *** Android NDK: Aborting    .  Stop.

2. Code writing

1) First, write java code

Create an Android application project HelloJni and create a file:

:

import ;
import ;
import ;

public class HelloJni extends Activity
{
  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState)
  {
    (savedInstanceState);

    TextView tv = new TextView(this);
    ( stringFromJNI() );
    setContentView(tv);
  }


  /* A native method that is implemented by the 'hello-jni' native library, which is packaged with this application. */
  public native String stringFromJNI();

  public native String unimplementedStringFromJNI();



  /* this is used to load the 'hello-jni' library on application startup. The library has already been unpacked into

   /data/data//lib/ at installation time by the package manager. */
  static {
    ("hello-jni");
  }
}

This code is very simple and the comments are very clear. Here are only two points:

static{
("hello-jni");
}

It indicates that when the program starts running, hello-jni will be loaded, and the code declared in the static area will be executed before the onCreate method. If you have multiple classes in your program, and if HelloJni is not the entry point of your application, then hello-jni (the full name is) will be loaded when you use HelloJni the first time.

public native String stringFromJNI();
public native String unimplementedStringFromJNI();

You can see that the declaration of these two methods has the native keyword. This keyword indicates that these two methods are local methods, which means that these two methods are implemented through local code (C/C++) and are only declared in java code.

Use eclipse to compile the project and generate the corresponding .class file. This step must be completed before the next step, because generating the .h file requires the corresponding .class file.

2) Write the corresponding C/C++ code

When I first started learning, there was a question that would be confusing. How to write the corresponding C/C++ code and how to define the function name? Here is a method, using the javah tool to generate the corresponding .h file, and then write the corresponding C/C++ code based on this .h file.

a. Generate the corresponding .h file:

Take my environment as an example. First, enter the directory of the HelloJni project you just established under the terminal:

braincol@ubuntu:~$ cd workspace/android/NDK/hello-jni/

ls View project files

braincol@ubuntu:~/workspace/android/NDK/hello-jni$ ls
  assets  bin    gen  res  src

You can see that there are only a few standard android applications currently (folders).

First, we create a jni folder in the project directory:

braincol@ubuntu:~/workspace/android/NDK/hello-jni$ mkdir jni
braincol@ubuntu:~/workspace/android/NDK/hello-jni$ ls
  assets  bin    gen  jni  res  src

The corresponding .h file can be generated below:

braincol@ubuntu:~/workspace/android/NDK/hello-jni$ javah -classpath bin -d jni
-classpath bin: represents the road strength of the class

-d jni: represents the directory where the generated header file is stored

It's the full class name

The success of this step is based on the fact that it has been generated in the bin/com/example/hellojni/  directory. You can now see that there is an additional .h file in the jni directory:

braincol@ubuntu:~/workspace/android/NDK/hello-jni$ cd jni/
braincol@ubuntu:~/workspace/android/NDK/hello-jni/jni$ ls
com_example_hellojni_HelloJni.h

Let's take a look at the content of com_example_hellojni_HelloJni.h:

com_example_hellojni_HelloJni.h :

/* DO NOT EDIT THIS FILE - it is machine generated */

#include <>

/* Header for class com_example_hellojni_HelloJni */

#ifndef _Included_com_example_hellojni_HelloJni

#define _Included_com_example_hellojni_HelloJni

#ifdef __cplusplus

extern "C" {

#endif

/*

 * Class:   com_example_hellojni_HelloJni

 * Method:  stringFromJNI

 * Signature: ()Ljava/lang/String;

 */

JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI

 (JNIEnv *, jobject);


/*

 * Class:   com_example_hellojni_HelloJni

 * Method:  unimplementedStringFromJNI

 * Signature: ()Ljava/lang/String;

 */

JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_unimplementedStringFromJNI

 (JNIEnv *, jobject);


#ifdef __cplusplus

}

#endif

#endif

The JNIEXPORT and JNICALL in the above code are jni macros, which are not needed in android jni, and of course there will be no mistakes when writing them. From the source code above, we can see that the function name is quite long. . . . However, it is still very regular, and is named completely in the form of: java_pacakege_class_mathod.

That is to say:

The stringFromJNI() method corresponds to the Java_com_example_hellojni_HelloJni_stringFromJNI() method in C/C++

The unimplementedStringFromJNI() method in C/C++ corresponds to the Java_com_example_hellojni_HelloJni_unimplementedStringFromJNI() method in C/C++

Note the comments:

Signature: ()Ljava/lang/String;

()Ljava/lang/String;() means that the parameter of the function is empty (the empty here means that there are no other parameters except JNIEnv * and jobject. JNIEnv* and jobject are two necessary parameters for all jni functions, representing the jni environment and the corresponding java class (or object) itself, respectively), Ljava/lang/String; means that the return value of the function is a String object of java.

b. Write the corresponding .c file:

:

#include <>
#include <>


/* This is a trivial JNI example where we use a native method

 * to return a new VM String. See the corresponding Java source

 * file located at:

 *  apps/samples/hello-jni/project/src/com/example/HelloJni/

 */

jstring Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env, jobject thiz )
{
  return (*env)->NewStringUTF(env, "Hello from JNI !");
}

Here we just implement the Java_com_example_hellojni_HelloJni_stringFromJNI method, and the Java_com_example_hellojni_HelloJni_unimplementedStringFromJNI method is not implemented, because only the stringFromJNI() method is called, so it doesn't matter if the unimplementedStringFromJNI() method is not implemented. However, it is recommended that all local methods defined in Java are implemented, and it is OK to write an empty function. . . It's better to have it than not.

The Java_com_example_hellojni_HelloJni_stringFromJNI() function simply returns a jstring object with content "Hello from JNI !" (corresponding to the String object in java). The file has been written, and now I can delete the com_example_hellojni_HelloJni.h file. Of course, it's okay to keep it, but I'm still used to cleaning up the unnecessary files.

3) Compile and generate the corresponding library

a Write a file

Create a new file in the jni directory (i.e., in the same level directory), the file is the Android makefile file, and the content is as follows:

# Copyright (C) 2009 The Android Open Source Project

#

# Licensed under the Apache License, Version 2.0 (the "License");

# you may not use this file except in compliance with the License.

# You may obtain a copy of the License at

#

#   /licenses/LICENSE-2.0

#

# Unless required by applicable law or agreed to in writing, software

# distributed under the License is distributed on an "AS IS" BASIS,

# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

# See the License for the specific language governing permissions and

# limitations under the License.

#

 
LOCAL_PATH := $(call my-dir)

 

include $(CLEAR_VARS)

 

LOCAL_MODULE  := hello-jni

LOCAL_SRC_FILES := 

 

include $(BUILD_SHARED_LIBRARY)

This file is very short, let's explain it line by line:

LOCAL_PATH := $(call my-dir)

A file must first define the LOCAL_PATH variable. It is used to find source files in the development tree. In this example, the macro function 'my-dir', provided by the compilation system, is used to return the current path (i.e., the directory containing the file file).

include $( CLEAR_VARS)

CLEAR_VARS is provided by the compilation system, specifying that GNU MAKEFILE clears many LOCAL_XXX variables for you (e.g. LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, etc...) except LOCAL_PATH. This is necessary because all compile control files are in the same GNU MAKE execution environment and all variables are global.

LOCAL_MODULE := hello-jni

The compiled target object, the LOCAL_MODULE variable must be defined to identify each module you describe in the file. The name must be unique and does not contain any spaces.

Note: The compilation system will automatically generate the appropriate prefix and suffix. In other words, a shared library module named 'hello-jni' will generate the '' file.

Important Note: If you name the library 'libhello-jni', the compilation system will not add any lib prefixes and will generate '', which is to support files from the Android platform source code, if you really need to do so.

LOCAL_SRC_FILES :=

The LOCAL_SRC_FILES variable must contain the C or C++ source code file to be compiled into the module. Note that you don't need to list the header and include files here, because the compilation system will automatically find the dependency files for you; just list the source code files that are directly passed to the compiler.

Note that the default C++ source file extension is '.cpp'. It is also possible to specify a different extension. Just define the LOCAL_DEFAULT_CPP_EXTENSION variable and don't forget the small dots at the beginning (that is, '.cxx', not 'cxx')

include $(BUILD_SHARED_LIBRARY)

BUILD_SHARED_LIBRARY means that the compiled and generated shared library is a variable provided by the compilation system, pointing to a GNU Makefile script, responsible for collecting all information defined in the LOCAL_XXX variable since the last call 'include $(CLEAR_VARS)', and decides what to compile and how to do it correctly. There is also the BUILD_STATIC_LIBRARY variable that indicates the generation of static libraries: lib$(LOCAL_MODULE).a, and BUILD_EXECUTABLE indicates the generation of executable files.

b. Generate .so shared library files

The Android file has been written, and now you can use the ndk-build script in the Android NDK development package to generate the corresponding .so shared library. The method is as follows:

braincol@ubuntu:~/workspace/android/NDK/hello-jni/jni$ cd ..
braincol@ubuntu:~/workspace/android/NDK/hello-jni$ ls
  assets  bin    gen  jni  libs  obj  res  src
braincol@ubuntu:~/workspace/android/NDK/hello-jni$ ndk-build
Gdbserver      : [arm-linux-androideabi-4.4.3] libs/armeabi/gdbserver
Gdbsetup       : libs/armeabi/
Install        : => libs/armeabi/

You can see that the shared library has been generated correctly. Let’s go to the libs/armeabi/ directory to see:

braincol@ubuntu:~/workspace/android/NDK/hello-jni$ cd libs/
braincol@ubuntu:~/workspace/android/NDK/hello-jni/libs$ ls
armeabi
braincol@ubuntu:~/workspace/android/NDK/hello-jni/libs$ cd armeabi/
braincol@ubuntu:~/workspace/android/NDK/hello-jni/libs/armeabi$ ls
gdbserver   

4) Recompile HelloJni project in eclipse and generate apk

Refresh the HelloJni project in eclipse, recompile and generate the apk, and the shared library will be packaged in the apk file together. Check out the running results in the simulator.

The above is the collection of information developed by Android NDK. We will continue to add relevant information in the future. Thank you for your support for this site!