Recently compiled the previous code on JNI, here to comb down, for future reference.
Introduction to JNI
JNI is the abbreviation for Java Native Interface, which provides a number of interfaces that enable the communication of Java and other languages (mainly C, C + +). Starting with Java1.1, the JNI standard becomes part of the Java platform, which allows Java code to interact with code written in other languages. JNI is a bridge between Java and C + + in Android, and JNI is a way for the JVM to interface with the native method.
Side effects of JNI
The two advantages of the Java platform are lost once the Jni,java program is used:
1, the program is no longer cross-platform. To cross-platform, you must recompile the local language section in a different system environment.
2, the program is no longer absolutely safe, the improper use of local code may cause the entire program to crash. A common rule is that you should let local methods focus on a few classes. This reduces the coupling between Java and C.
JNI Usage Scenarios
When you start to prepare a project that uses JNI, make sure there are alternatives. Application use of JNI can have some side effects. Here are a few scenarios where you can avoid using JNI to interact with your local code:
1. Java programs and local programs use TCP/IP or IPC to interact.
2. Use the API provided by JDBC when connecting to a local database with a Java program.
3. Java programs can use distributed object technology, such as the Java IDL API.
What these scenarios have in common is that Java and C are on different threads, or on different machines. This way, when the local program crashes, the Java program is not affected.
In the following scenarios, the use of JNI within the same process cannot be avoided:
1, the program uses the Java API does not provide a special system environment will have the characteristics. And the process of operation is not realistic.
2. You may want to access some of your own local libraries, but don't want to pay the cost of cross-process calls, such as efficiency, memory, and data transfer.
3, Java program part of the code on the efficiency requirements are very high, such as algorithm calculation, graphics rendering and so on.
In summary, use JNI only when you must invoke local code in the same process.
JNI Registration method
There are two ways to register: static and Dynamic registration
Static registration
1, define the native method in the Java file.
2, switch directories in cmd command-line mode to the definition native method class file (or Java file) storage location.
3, use the Javah and Javac commands to generate the. h header file that contains the native method.
4, implement the native method, build the. So library with Ndk-build compilation.
Static Registration Method Step is cumbersome, in the project I prefer the dynamic registration method.
Dynamically registering JNI
First, create an Android project and tick the content in the include C + + Support,mainactivity.java:
public class MainActivity extends AppCompatActivity {
// Used to load the ‘native-lib’ library on application startup.
static {
System.loadLibrary ("JNITest");
}
public String TAG = "MainActivity";
public JNITest jniTest;
TextView mTextView;
@Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate (savedInstanceState);
setContentView (R.layout.activity_main);
TextView tv = (TextView) findViewById (R.id.sample_text);
mTextView = (TextView) findViewById (R.id.textView);
jniTest = new JNITest ();
int sum = jniTest.addInt (4,3);
// Example of a call to a native method
mTextView.setText (jniTest.getString () + "" + sum);
Log.d (TAG, "set text after ....");
}
}
Then create a java file to define the native method required, here define two methods
package com.example.szq.testjni;
public class JNITest {
public JNITest () {
}
public native String getString ();
public native int addInt (int a, int b);
}
Create a jni folder in the src / main directory, and create two new files JNITest.c and Android.mk
#include <stdio.h>
#include <stdlib.h>
#include <jni.h>
#include <assert.h>
#define JNIREG_CLASS "com / example / szq / testjni / JNITest" // java file that defines the native method
//achieve
jstring jni_getstr (JNIEnv * jniEnv, jobject ob)
{
return (* jniEnv)-> NewStringUTF (jniEnv, "Dynamic Registration JNI test");
}
jint jni_add (JNIEnv * jniEnv, jobject ob, jint a, jint b)
{
return a + b;
}
static JNINativeMethod gMethods [] = {
{"getString", "() Ljava / lang / String;", (void *) jni_getstr},
{"addInt", "(II) I", (void *) jni_add},
};
static int registerNativeMethods (JNIEnv * env
, const char * className
, JNINativeMethod * gMethods, int numMethods) {
jclass clazz;
clazz = (* env)-> FindClass (env, className);
if (clazz == NULL) {
return JNI_FALSE;
}
if ((* env)-> RegisterNatives (env, clazz, gMethods, numMethods) <0) {
return JNI_FALSE;
}
return JNI_TRUE;
}
/ *
* Register local methods for all classes
* /
static int registerNatives (JNIEnv * env) {
int re = registerNativeMethods (env, JNIREG_CLASS, gMethods,
sizeof (gMethods) / sizeof (gMethods [0]));
return re;
}
/ *
* Called when System.loadLibrary ("lib")
* If the JNI version is successfully returned, -1 if it fails
* /
JNIEXPORT jint JNICALL JNI_OnLoad (JavaVM * vm, void * reserved) {
JNIEnv * env = NULL;
jint result = -1;
if ((* vm)-> GetEnv (vm, (void **) & env, JNI_VERSION_1_6)! = JNI_OK) {
return -1;
}
assert (env! = NULL);
if (! registerNatives (env)) {// Register
return -1;
}
//success
result = JNI_VERSION_1_6;
return result;
}
Configure Android.mk
LOCAL_PATH: = $ (call my-dir)
include $ (CLEAR_VARS)
LOCAL_MODULE: = JNITest
LOCAL_SRC_FILES: = JNITest.c
LOCAL_LDFLAGS + = -llog
include $ (BUILD_SHARED_LIBRARY)
Switch to the jni directory in the command line mode, run ndk-build to generate the .so library (provided that the ndk environment is configured first), copy the .so file to src / main / libs, build the project, you can run the following the result of:
Note: The generated .so file must be in the libs folder on the same layer as jni
NDK automatic compilation configuration
Configure build.gradle, because when the project is built, the compiler will automatically load the gradle file, so adding the compilation task (task) in gradle can compile the c file in jni. The configuration is as follows:
apply plugin: ‘com.android.application’
android {
compileSdkVersion 26
defaultConfig {
applicationId "com.example.szq.testjni"
minSdkVersion 18
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
ndk {
moduleName "JNITest"
ldLibs "log", "z", "m"
abiFilters "armeabi", "armeabi-v7a", "x86"
// Used to specify which standard library the application should use, add c ++ library support here
stl "stlport_static" // supports stl
cFlags "-fexceptions" // support exception
}
tasks.withType (JavaCompile) {
compileTask-> compileTask.dependsOn ‘ndkBuild’, ‘copyJniLibs’
}
sourceSets.main {
jniLibs.srcDirs = [‘libs’]
}
externalNativeBuild {
cmake {
cppFlags ""
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile (‘proguard-android.txt’), ‘proguard-rules.pro’
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
task ndkBuild (type: Exec) {
// def ndkDir = project.plugins.findPlugin (‘com.android.application’). sdkHandler.getNdkFolder ()
def ndkDir = project.android.ndkDirectory
commandLine "$ ndkDir \\ ndk-build.cmd", ‘-C’, ‘src / main / jni’,
"NDK_OUT = $ buildDir / ndk / obj",
"NDK_APP_DST_DIR = $ buildDir / ndk / libs / \ $ (TARGET_ARCH_ABI)"
}
task copyJniLibs (type: Copy) {
from fileTree (dir: file (buildDir.absolutePath + ‘/ ndk / libs’), include: ‘** / *. so’)
into file (‘src / main / jniLibs’)
}
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 ‘
}
In this way, the project is built directly, and the program can be run after the construction is completed.
In the Android studio 3.0 version, a more convenient CMake is added to compile jni. The configuration file is CMakeLists.txt. CMake will be used frequently in future projects. Interested parties can study it together.
Android dynamic registration jni