Why the NDK?

Generally speaking, it can be divided into the following situations:

1. Code protection, because apK Java layer code is easy to be decompiled, but C/C++ library integration is more difficult.

2. Call third party C/C++ libraries in NDK, since most open source libraries are written in C/C++ code.

3. Easy to transplant, the library written in C/C++ can be easily used in other embedded platforms.

Here’s how to get started with Android NDK:

The best way to get started is to learn the Android demo, which comes with the Android NDK: Hello-jni.

I. Establishment of development environment

  1. Android NDK development needs to be done under Linux: because you need to write C/C++ code to generate.so files that can run on ARM, you need to use a cross-compilation environment, and cross-compilation can only be done under Linux.
  2. Install the Android-NDK development kit, which can be downloaded from Google Android: you can compile the C/C++ code of android JNI into a library using this kit
  3. Android application development environment: including Eclipse, Java, Android SDK, ADT, etc.

How to install and download android- NDK, I will not be bothersome, after installing android- NDK, you need to add the PATH to the environment variable PATH:

sudo gedit /etc/environment

Add your Android-NDK installation PATH to the environment’s PATH variable and let the change take effect immediately:

 source  /etc/environment

After the above steps, type on the command line:

ndk-bulid

If the following error message is displayed instead of saying ndK-build not found, 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/build-local.mk:85: *** Android NDK: Aborting    .  Stop.

 

Second, code writing

1. Write Java code first

Create an Android application project HelloJni and create a helloJni.java file:

HelloJni.java :

/*
 * 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
 *
 *      http://www.apache.org/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.
 */
package com.example.hellojni;

import android.app.Activity;
import android.widget.TextView;
import android.os.Bundle;


public class HelloJni extends Activity

{
/** Called when the activity is first created. */

@Override
public void onCreate(Bundle savedInstanceState)

{
super.onCreate(savedInstanceState);

/* Create a TextView and set its content.
         * the text is retrieved by calling a native
         * function.
         */

TextView  tv = new TextView(this);

tv.setText( 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();

/* This is another native method declaration that is *not*
     * implemented by 'hello-jni'. This is simply to show that
     * you can declare as many native methods in your Java code
     * as you want, their implementation is searched in the
     * currently loaded native libraries only the first time
     * you call them.
     *
     * Trying to call this function will result in a
     * java.lang.UnsatisfiedLinkError exception !
     */
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/com.example.HelloJni/lib/libhello-jni.so at
     * installation time by the package manager.
     */
static {

System.loadLibrary("hello-jni");

}

}Copy the code

The code is very simple and clearly commented, but here are just two points:

static{ 

System.loadLibrary(“hello-jni”); 

}

Hello-jni: hello-jni: hello-jni: hello-jni: hello-jni: hello-jni: hello-jni: hello-jni: hello-jni: hello-jni If you have multiple classes in your application, and HelloJni is not the entry point to your application, the hello-jni (full name is libhello-jni.so) library will be loaded the first time you use HelloJni.

public native String stringFromJNI(); 

public native String unimplementedStringFromJNI();

You can see the native keyword in the declarations of these two methods. This keyword indicates that these two methods are native methods, that is, they are implemented by native code (C/C++). In Java code, they are only declarations.

Compile the project with Eclipse and generate the corresponding.class files, which must be done before the next step because the corresponding.class files are needed to generate the.h files.

 

2. Write corresponding C/C++ code

At the beginning, there is a question that will make people very confused, how to write the corresponding C/C++ code, how to define the function name? Here is a way to use the javah tool to generate the corresponding.h file, and then write the corresponding C/C++ code based on the.h file.

2.1 Generate corresponding. H files:

Take my environment as an example, first enter the directory of the HelloJni project just created under the terminal:

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

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

AndroidManifest.xml  assets  bin  default.properties  gen  res  src 

You can see that there are only a few files (folders) for standard Android applications.

First we create a JNI folder under the project directory:

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

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

AndroidManifest.xml  assets  bin  default.properties  gen  jni  res  src 

The corresponding.h file can be generated as follows:

braincol@ubuntu:~/workspace/android/NDK/hello-jni$ javah -classpath bin -d jni com.example.hellojni.HelloJni 

-classpath bin: indicates the path strength of a class

-d jni: indicates the directory for storing the generated header file

Com. Example. Hellojni. Hellojni is the full name of the class

The success of this step is to build in already in the bin/com/example/hellojni/directory or become hellojni. On the basis of the class.

Now you can see that there is a.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 look at the contents of com_example_HELLOJni_hellojni.h:

com_example_hellojni_HelloJni.h :

/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* 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 #endifCopy the code

JNIEXPORT and JNICALL in the code above are jNI macros, which are not needed in android JNI.

From the above source can be seen in the function name that is quite long ah… But it’s very regular, and it’s named exactly as java_pacakege_class_mathod.

In other words:

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

HelloJni. In Java unimplementedStringFromJNI () method of the corresponding to the C/C + + Java_com_example_hellojni_HelloJni_unimplementedStringFromJNI () method

Note the note below:

Signature: ()Ljava/lang/String; 

()Ljava/lang/String;

JNIEnv* and jobject are mandatory for all JNI functions, representing the jNI environment and the corresponding Java class (or object) itself.

Ljava/lang/String; Indicates that the return value of the function is a Java String.

 

2.2 Prepare corresponding. C files:

hello-jni.c :

/* * 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 * * http://www.apache.org/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. * */ #include <string.h> #include <jni.h> /* 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/HelloJni.java */ jstring Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env, jobject thiz ) { return (*env)->NewStringUTF(env, "Hello from JNI !" ); }Copy the code

Here we just implement the Java_com_example_hellojni_HelloJni_stringFromJNI method, And Java_com_example_hellojni_HelloJni_unimplementedStringFromJNI method did not realize, because in HelloJni. Just call the stringFromJNI in Java () method, So unimplementedStringFromJNI () method does not realize it doesn’t matter, but suggest it is better to put all the native methods are defined in the Java implementation, write a line and an empty function… Something is better than nothing.

The Java_com_example_hellojni_HelloJni_stringFromJNI() function simply returns “Hello from JNI!” Is a JString object (corresponding to a String object in Java).

The hello-jni.c file is already written. Now I can delete the com_example_hellojni_hellojni. h file, or keep it, but I’m still in the habit of cleaning up files that I don’t need.

 

3. Compile Hello-jni. c to generate the corresponding library

3.1 Writing the Android.mk file

Create a new android. mk file in the jni directory. The android. mk file is an Android makefile and contains the following contents:

Licensed under The Apache License, Version 2.0 (The "License") # Copyright (C) 2009 The Android Open Source Project # # Licensed under The Apache License;  # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/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 := hello-jni.c include $(BUILD_SHARED_LIBRARY)Copy the code

LOCAL_PATH := $(call my-dir)

An Android.mk file must first define the LOCAL_PATH variable. It is used to find source files in the development tree. In this case, the macro function ‘my-dir’, provided by the compilation system, returns the current path (that is, the directory containing the android.mk file).

include $( CLEAR_VARS)

CLEAR_VARS is provided by the compilation system, specifying that the GNU MAKEFILE will clear many LOCAL_XXX variables for you (such as LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, etc…). , in addition to 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 target object for compilation, the LOCAL_MODULE variable, must be defined to identify each module you describe in the Android.mk file. The name must be unique and contain no Spaces.

Note: The compilation system automatically generates the appropriate prefixes and suffixes, in other words, a shared library module named ‘hello-jni’ will generate the ‘libhello-jni.so’ file.

Important Notes:

If you name the library ‘libhello-jni ‘, the compiler will not add any lib prefixes and will generate ‘libhello-jni.so’ in order to support the Android.mk files from the Android platform source code, if you need to do so.

LOCAL_SRC_FILES := hello-jni.c

The LOCAL_SRC_FILES variable must contain the C or C++ source files to be compiled and packaged into the module. Note that you do not need to list header and include files here, because the compiler will automatically find dependent files for you; Just list the source code files that are passed directly to the compiler.

Note that the default C++ source file has a ‘.cpp ‘extension. It is also possible to specify a different extension by defining the LOCAL_DEFAULT_CPP_EXTENSION variable and not forgetting the starting dot (i.e.’.cxx ‘instead of’ CXX ‘).

include $(BUILD_SHARED_LIBRARY)

BUILD_SHARED_LIBRARY is a variable provided by the build system that points to a GNU Makefile script that collects all information defined in the LOCAL_XXX variable since the last call to ‘include $(CLEAR_VARS)’. And decide what to compile and how to do it correctly. There are also BUILD_STATIC_LIBRARY variables that generate static libraries: lib$(LOCAL_MODULE).a and BUILD_EXECUTABLE variables that generate executable files.

3.2 Generate the. So shared library file

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

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

AndroidManifest.xml  assets  bin  default.properties  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/gdb.setup 

Install        : libhello-jni.so => libs/armeabi/libhello-jni.so 

Libs /armeabi/ libhello-jni.so shared library libhello-jni.so shared library libhello-jni.so shared library libhello-jni.so shared library libhello-jni.so shared library libhello-jni.so shared library

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  gdb.setup  libhello-jni.so

4. Recompile HelloJni project in Eclipse to generate APK

Eclipse refreshes the HelloJni project and recompiles it to generate APK. The libhello-jni.so shared library is packaged together in the APK file.

See the results in the simulator: