Android JNI programming and NDK

Lead

Java JNI intended for the Java Native Interface (java native interface), is to facilitate the java call C or C ++ native code such as encapsulated layer interface. Since the cross-platform Java led to bad, some of the operating system and associated local interactive capabilities the characteristics of Java can not be completed, so Java JNI provides a dedicated and native code interactions.

main content

  • JNI development process
  • NDK development process
  • JNI data types and type signature
  • JNI to call Java methods of process

specific contents

NDK is a tool collection of Android offered, can be more easily accessed by NDK in Android through JNI native code. NDK also provides a cross-compiler tools, developers need only simple modifications mk file can generate a specific CPU platform . dynamic library the following benefits:

  • Protection code. Since the java-level code apk easily be decompiled and C / C ++ libraries decompile difficult.
  • You can easily use the C / C ++ open source library.
  • Easy to transplant, using C / C ++ libraries can easily use to write again on other platforms
  • Providers in efficiency under certain circumstances, but did not significantly improve the performance of Android programs.

JNI development process

In Java method declaration natvie

Creating a class

Life of two native methods: get and set (String). This is the approach needs to be implemented in the JNI. JniTest head there is a process of dynamic loading, loading the library name so filled although jni-test, but so full library name should be libjni-test.so, it is loaded so the library specification.

Edit Java source files get class file, and then export the JNI header file by command javah

In the root of the package, a command operation

javac com/szysky/note/androiddevseek_14/JNITest.java
javah com.szysky.note.androiddevseek_14.JNITest

After performing com_szysky_note_androiddevseek_14_JNITest.h will generate a header file, under the path of the operation, this is what generated the second step:

  • Function name: format follows: between Java class package name name name Method divided package names all replaced segmentation.
  • Parameters: jstring String type parameter is representative of a particular type of relationship will explain later.
    • JNIEnv *: indicates a pointer to the JNI environment, which can be provided by the access method JNI.
    • jobject: java object representation of this.
    • JNIEXPORT and JNICALL: This is a macro JNI types defined jni.h can be found in the header file.
#ifdef __cplusplus
extern "C" {
#endif

And this macro definition is necessary, is to function inside the designated extern "C" using C language naming style to compile. If set then when the JNI using C ++ to achieve, because the C / C ++ compilation naming style for function different, which will lead to JNI can not find a specific function based on the function name in the link, JNI calls will certainly fail.

With C / C ++ method implemented natvie

JNI native method is a method refers to Java in the statement, where you can choose to implement c ++ and c. The process is similar. Only a small amount of difference, both of which are here to achieve it.

Created in the home directory in a subdirectory of the project, any name, and then copy the previously generated by javah command .h header file to the directory you created, and then create test.cpp test.c two files, to achieve the following:

#include "com_szysky_note_androiddevseek_14_JNITest.h"
#include <stdio.h>
JNIEXPORT jstring JNICALL Java_com_szysky_note_androiddevseek_114_JNITest_get(JNIEnv *env, jobject thiz){
    printf("执行在c++文件中 get方法\n");
    return env->NewStringUTF("Hello from JNI .");
}
JNIEXPORT void JNICALL Java_com_szysky_note_androiddevseek_114_JNITest_get(JNIEnv *env, jobject thiz, jstring string){
    printf("执行在c++文件中 set方法\n");
    char* str = (char*) env->GetStringUTFChars(string, NULL);
    printf("\n, str");
    env->ReleaseStringUTFChars(string, str);
}
#include "com_szysky_note_androiddevseek_14_JNITest.h"
#include <stdio.h>
JNIEXPORT jstring JNICALL Java_com_szysky_note_androiddevseek_114_JNITest_get(JNIEnv *env, jobject thiz){
    printf("执行在c文件中 get方法\n");
    return (*env)->NewStringUTF("Hello from JNI .");
JNIEXPORT void JNICALL Java_com_szysky_note_androiddevseek_114_JNITest_get(JNIEnv *env, jobject thiz, jstring string){
    printf("执行在c文件中 set方法\n");
    char* str = (char*) (*env)->GetStringUTFChars(env, string, NULL);
    printf("%s\n, str");
    (*env)->ReleaseStringUTFChars(env, string, str);
}}

In fact, C \ C ++ is very similar in implementation, but for a different mode of operation env.

C++: env->ReleaseStringUTFChars(string, str);
C:  (*env)->ReleaseStringUTFChars(env, string, str); 
Compile so the library and call in java

compiled library used here so GCC. cd command to generate just placed under c / c ++ directory.
Use the following command:

gcc -shared -I /user/lib/jvm/java-7-openjdk-amd64/include -fPIC test.cpp -o libjni-test.so
gcc -shared -I /user/lib/jvm/java-7-openjdk-amd64/include -fPIC test.c -o libjni-test.so

/ User / lib / jvm / java-7-openjdk-amd64 is the installation path of the local jdk, libjni-test.so so is the name of the production database. Java by: System.loadLibrary ( "jni-test") is loaded, which lib and .so do not need to point out.

Switch to the home directory, a Java program executed by the Java instructions: java -Djava.library.path = jni com.ryg.JniTest. Wherein -Djava.library.path = jni library so specified path.

NDK development process

Download and configure the NDK

Download NDK good development kit NDK and configure global variables.

Create an Android project, and declare the required native method
public static native String getStringFromC();
Implement native methods declared in the Android project
  1. Generate C / C ++ header files
    open the console, use the cd command to switch the current directory to the current project,
    generate a header file using the command javah.
javah -classpath bin\classes;C:\MOX\AndroidSDK\platforms\android-23\android.jar -d jni cn.hudp.hellondk.MainActivity

Description: bin \ classes class file as a relative path of the project; C: \ MOX \ AndroidSDK \ platforms \ android-23 \ android.jar android.jar full path, because we use the Activity to the Android SDK, it generates he needed header files; -d jni is the output generated header files to the jni folder of the project; and finally with the cn.hudp.hellondk.MainActivity is the package name and class name of the native method where the class.

  1. Preparation of modified files corresponding android.mk (mk file is the configuration file used to develop NDK)
# 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.
 # L
 OCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
 ## 对应Java部分 System.loadLibrary(String libName) 的libname
 LOCAL_MODULE := hello
 
 ## 对应c/c++的实现文件名
 LOCAL_SRC_FILES := hello.c
 include $(BUILD_SHARED_LIBRARY)
  1. Write Application.mk, to specify the dynamic library to be generated corresponding platform, here is the full platform support, you can also specify special. Current common architecture platform armeabi, x86 and mips. Wherein the mobile device is mainly armeabi, so most apk contains only the so armeabi library.
APP_ABI := all
Jni switch to the parent directory of the directory, and then compile the library so produced by ndk-build command

ndk-build command to specify the default jni directory for the local source directory.
The lower compiled so the library project into the Android app / src / main / jniLbis directory, or set up a new store so the library catalog through the app gradle follows:

android{
    ……
    sourceSets.main{
        jniLibs.srcDir 'src/main/jni_libs'
    }
}

You can also add NDK options by defaultConfig area:

android{
    ……
    defaultConfig{
        ……
        ndk{
            moduleName "jni-test"
        }
    }
}

Dynamic packaging may also be different CPU platforms apk corresponding intake and so disposed in productFlavors (APK volume reduction):


gradle
android{
    ……
    productFlavors{
        arm{
            ndk{
                adiFilter "armeabi"
            }
        } 
        x86{
            ndk{
                adiFilter "x86"
            }
        }
    }
}

Call in Android:

public class MainActivity extends Activity {
    public static native String getStringFromC();
    static{//在静态代码块中调用所需要的so文件,参数对应.so文件所对应的LOCAL_MODULE;
        System.loadLibrary("hello");
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //在需要的地方调用native方法
        Toast.makeText(getApplicationContext(), get(), Toast.LENGTH_LONG).show();
    }
}

More Reference

JNI data types and type signature

JNI contains two data types: basic and reference types.
The basic types are jboolean, jchar, jint the like, and the corresponding data types in Java follows:

JNI type Java type description
jboolean boolean Unsigned 8-bit integer
jbyte byte Unsigned 8-bit integer
Jcr char 16-bit unsigned integer
jshort short Signed 16-bit integer
jint int 32-bit integer
jlong long 64-bit integer
jfloat float 32-bit floating-point
jdouble double 64-bit floating point
void void Untyped

JNI references in the main types of classes, objects and arrays them in Java and reference types of correspondence is as follows:

JNI type Java type description
jobject Object Object Types
jclass Class Class Type
jstring String String type
jobjeckArray Object[] An array of objects
jbooleanArray boolean[] boolean arrays
jbyteArray byte[] byte array
jcharArray char[] char array
jshortArray short[] short array
jintArray int[] int array
jlongArray long[] long array
jfloatArray float[] float array
jdoubleArray double[] double array
jthrowable Throwable Throwable

JNI type signature identifies a specific Java type, which can be either class can be a method, can also be a data type.

Relatively simple class signature, which uses "L + type + + package name;" form, which only need to replace / can e.g. java.lang.String, its signature Ljava / lang / String ;,.. end; also part of.

The basic data types using a series of signatures in capital letters, as follows:

JNI type Java type description JNI type Java type description
boolean WITH byte B char C
short S int I long J
float F double D void V

The basic data types are basically signing the first letter of a word, but except boolean has been occupied since the B byte, while the long representation was also occupied by a Java class signature so different.

And the array of objects, the object is signed object belongs to the class signature, the array [e.g. + type signature byte array. First type is byte, then B is so because the signature is a signature of the array so that the final form [e.g. follows B. various correspondence:

char[]      [C
float[]     [F
double[]    [D
long[]      [J
String[]    [Ljava/lang/String;
Object[]    [Ljava/lang/Object;

If the number of multi-dimensional arrays then how to decide [according to the dimensions of the array, such as int [] [] so that [[I

  • The method of signature (signature parameter type) returns the value + type signature.
    Method boolean fun (int a, double b , int [] c). Parameters are linked to the type of signature, it is in accordance with the method signature rule (ID [I) Z
  • Method: void fun (int a, String s, int [] c), then the signature is (ILjava / lang / String; [I) V
  • Method: int fun (), the corresponding signature () the I
  • Method: int fun (float f), the corresponding signature (F) I

JNI to call Java methods of process

JNI call java method of procedure is to first find the class by class name, and then find ways according to the method name id, and finally you can call this method a. If it is non-static Java method calls, then the need to construct an object class before you can call it.

Show you a static method call:

  1. First, declare static methods to be invoked in java. Here is the opportunity to trigger a click of a button, add their own.
static{
         System.loadLibrary("jni-test");
     }
 /**
  * 定义一个静态方法 , 提供给JNI调用
  */
 public static void methodCalledByJni(String fromJni){
     Log.e("susu", "我是从JNI被调用的消息,  JNI返回的值是:"+fromJni );
 }
 // 定义调用本地方法, 好让本地方法回调java中的方法
 public native void callJNIConvertJavaMethod();
 @Override
 public void onClick(View view) {
     switch (view.getId()){
         case R.id.btn_jni2java:
             // 调用JNI的方法
             callJNIConvertJavaMethod();
             break;
     }
 }
  1. Add a c test.cpp in the JNI functions for handling the call java logic, and to provide a method for adjusting up trigger java code. A total of two methods.
// 定义调用java中的方法的函数
 void callJavaMethod( JNIEnv *env, jobject thiz){
     // 先找到要调用的类
     jclass clazz = env -> FindClass("com/szysky/note/androiddevseek_14/MainActivity");
     if (clazz == NULL){
         printf("找不到要调用方法的所属类");
         return;
     }
     // 获取java方法id
     // 参数二是调用的方法名,  参数三是方法的签名
     jmethodID id = env -> GetStaticMethodID(clazz, "methodCalledByJni", "(Ljava/lang/String;)V");
     if (id == NULL){
         printf("找不到要调用方法");
         return;
     }
     jstring msg = env->NewStringUTF("我是在c中生成的字符串");
     // 开始调用java中的静态方法
     env -> CallStaticVoidMethod(clazz, id, msg);
 }
 void Java_com_szysky_note_androiddevseek_114_MainActivity_callJNIConvertJavaMethod(JNIEnv *env, jobject thiz){
     printf("调用c代码成功, 马上回调java中的代码");
     callJavaMethod(env, thiz);
 }

Explain a little, the program first to find the class name com / szysky / note / androiddevseek_14 / MainActivity class, and then find a way to methodCalledByJni in accordance with the method name, and pass the corresponding method signature (Ljava / lang / String;), and finally by JNIEnv object CallStaticVoidMethod () method to complete the final call.

Finally, you can just call callJavaMethod method in Java_com_szysky_note_androiddevseek_114_MainActivity_callJNIConvertJavaMethod method.

Process is -> button to trigger the onClikc click -> then Java will call the JNI callJNIConvertJavaMethod () -> JNI's callJNIConvertJavaMethod () method internally calls the specific method callJavaMethod callback in Java () -> method final by CallStaticVoidMethod ( ) calls in Java methodCalledByJni () to receive a parameter and print a log.

Generated so git repository file is saved in the app / src / main / backup directory next two versions of the code, the first is the development code NDK second section, the second is the fourth section of the code is the current and so the library is up to date, it contains all the code generation JNI libraries.

JNI to call Java procedures and methods defined in Java have great relevance for different types of java method, JNIEnv provides a different interface to call, some going to develop more details or go to the website to learn more.

More poke here (tidied various anthology)

Guess you like

Origin blog.csdn.net/weixin_34112208/article/details/90837735