JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++)。从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他编程语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的。例如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少要保证本地代码能工作在任何Java 虚拟机环境。
JNINativeMethod结构体中有一个字段为signature(签名),再介绍signature格式之前需要掌握各种数据类型在Java层、Native层以及签名所采用的签名格式。
Signature格式 | Java | Native |
---|---|---|
B | byte | jbyte |
C | char | jchar |
D | double | jdouble |
F | float | jfloat |
I | int | jint |
S | short | jshort |
J | long | jlong |
Z | boolean | jboolean |
V | void | void |
数组简称则是在前面添加[:
Signature格式 | Java | Native |
---|---|---|
[B | byte[] | jbyteArray |
[C | char[] | jcharArray |
[D | double[] | jdoubleArray |
[F | float[] | jfloatArray |
[I | int[] | jintArray |
[S | short[] | jshortArray |
[J | long[] | jlongArray |
[Z | boolean[] | jbooleanArray |
对象类型简称:L+classname +;
Signature格式 | Java | Native |
---|---|---|
Ljava/lang/String; | String | jstring |
L+classname +; | 所有对象 | jobject |
[L+classname +; | Object[] | jobjectArray |
Ljava.lang.Class; | Class | jclass |
Ljava.lang.Throwable; | Throwable | jthrowable |
有了前面的铺垫,那么再来通过实例说说函数签名: (输入参数…)返回值参数,这里用到的便是前面介绍的Signature格式。
Java函数 | 对应的签名 |
---|---|
void foo() | ()V |
float foo(int i) | (I)F |
long foo(int[] i) | ([I)J |
double foo(Class c) | (Ljava/lang/Class;)D |
boolean foo(int[] i,String s) | ([ILjava/lang/String;)Z |
String foo(int i) | (I)Ljava/lang/String; |
咱们这里先创建一个AS工程如下图:
创建JavaNativeScheduler 类
package com.pengtg.jnidemo;
import android.util.Log;
public class JavaNativeScheduler {
public static final String TAG = "JavaNativeScheduler";
public showToastCallback mCallback;
public JavaNativeScheduler(showToastCallback callback){
mCallback = callback;
}
public native void setFlag(int flag); //创建setFlag 的native方法
public void updateTextFromNative(int flag){ //此方法是供C++调用的
Log.d(TAG,"I'm called from native!!! set flag success!!!");
mCallback.showToast(flag);
}
interface showToastCallback{//MainActivity实现该接口
void showToast(int flag);
}
}
通过命令:javah -jni com.pengtg.jnidemo.JavaNativeScheduler 生成 jni 头文件com_pengtg_jnidemo_JavaNativeScheduler.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_pengtg_jnidemo_JavaNativeScheduler */
#include "../cpp/my_preview/preview_controller.h"
#ifndef _Included_com_pengtg_jnidemo_JavaNativeScheduler
#define _Included_com_pengtg_jnidemo_JavaNativeScheduler
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_pengtg_jnidemo_JavaNativeScheduler
* Method: setFlag
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_com_pengtg_jnidemo_JavaNativeScheduler_setFlag
(JNIEnv *, jobject, jint);
#ifdef __cplusplus
}
#endif
#endif
根据com_pengtg_jnidemo_JavaNativeScheduler.h头文件,我们创建com_pengtg_jnidemo_JavaNativeScheduler.cpp
并实现Java_com_pengtg_jnidemo_JavaNativeScheduler_setFlag的JNI方法
JNIEXPORT void JNICALL Java_com_pengtg_jnidemo_JavaNativeScheduler_setFlag
(JNIEnv *env, jobject obj, jint flag){
controller = new MyController();
JavaVM *g_jvm = NULL;
env->GetJavaVM(&g_jvm);
g_obj = env->NewGlobalRef(obj);
controller->prepareContext(g_jvm,g_obj,flag);
}
这里我们创建了MyController对象,并调用了prepareContext方法。下面我们来看一下MyController的声明和定义部分。preview_controller.h
//
// Created by pengt on 2018/8/21.
//
#ifndef JNIDEMO_PREVIEW_CONTROLLER_H
#define JNIDEMO_PREVIEW_CONTROLLER_H
#include <jni.h>
#include <pthread.h>
class MyController {
public:
MyController();
virtual ~MyController();
void prepareContext(JavaVM *g_jvm, jobject obj, int flag);
static void *threadStartCallback(void *myself);
void updateText();
protected:
JavaVM *g_jvm;
jobject obj;
int flag;
pthread_t _thread_id;
};
#endif //JNIDEMO_PREVIEW_CONTROLLER_H
preview_controller.cpp
// An highlighted block
//
// Created by pengt on 2018/8/21.
//
#include <jni.h>
#include <stdlib.h>
#include "preview_controller.h"
#include "../log.h"
#include <pthread.h>
#define LOG_TAG "MyController"
MyController::MyController() {
LOGI("MyController instance created");
}
MyController::~MyController() {
LOGI("MyController instance destroyed");
}
void MyController::prepareContext(JavaVM *g_jvm, jobject obj, int flag) {
LOGI("MyController prepareContext");
this->g_jvm = g_jvm;
this->obj = obj;
this->flag = flag;//至此java调用C++将flag值赋给了C++层
pthread_create(&_thread_id, NULL, threadStartCallback, this);//
}
void *MyController::threadStartCallback(void *myself) {
MyController *controller = (MyController *)myself;
controller->updateText();
pthread_exit(0);
}
void MyController::updateText() {
LOGI("MyController updateText");
JNIEnv *env;
if(g_jvm->AttachCurrentThread(&env, NULL) != JNI_OK){
LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
return;
//goto error;
}
g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4);
if(env == NULL){
LOGI("get JNIEnv failed");
return;
//goto error;
}
jclass jcls = env->GetObjectClass(obj);
if(NULL != jcls){
jmethodID updateTextCallback = env->GetMethodID(jcls,"updateTextFromNative", "(I)V");
if(NULL != updateTextCallback){
env->CallVoidMethod(obj,updateTextCallback,flag);
}
}
//error:
if(g_jvm->DetachCurrentThread() != JNI_OK){
LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
}
pthread_exit(0);
}
接下来我们在MainActivity 中进行java的JNI调用:
package com.pengtg.jnidemo;
import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity implements JavaNativeScheduler.showToastCallback {
public Context mContext;
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
// Example of a call to a native method
TextView tv = (TextView) findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
JavaNativeScheduler scheduler = new JavaNativeScheduler(this);
scheduler.setFlag(1);
}
@Override
public void showToast(final int flag) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(mContext,"set flag: " + flag + " success!!!", Toast.LENGTH_LONG).show();
}
});
}
}
void MyController::prepareContext(JavaVM *g_jvm, jobject obj, int flag) {
LOGI("MyController prepareContext");
this->g_jvm = g_jvm; //JavaVM:是指进程虚拟机环境,每个进程有且只有一个JavaVM实例
this->obj = obj; //JNIEnv:是指线程上下文环境,每个线程有且只有一个JNIEnv实例。
//obj是在JNI方法Java_com_pengtg_jnidemo_JavaNativeScheduler_setFlag中赋值过来的env->NewGlobalRef(obj)
this->flag = flag;//至此java调用C++将flag值赋给了C++层
pthread_create(&_thread_id, NULL, threadStartCallback, this);//我们这里创建新的线程用来执行调用Java方法。
}
void *MyController::threadStartCallback(void *myself) {
MyController *controller = (MyController *)myself;
controller->updateText();
pthread_exit(0);
}
void MyController::updateText() {
LOGI("MyController updateText");
JNIEnv *env;
if(g_jvm->AttachCurrentThread(&env, NULL) != JNI_OK){
LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
return;
//goto error;
}
g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4);//获取上下文JNIEnv
if(env == NULL){
LOGI("get JNIEnv failed");
return;
//goto error;
}
jclass jcls = env->GetObjectClass(obj);//通过赋值过来的对象获得native类
if(NULL != jcls){
jmethodID updateTextCallback = env->GetMethodID(jcls,"updateTextFromNative", "(I)V");//获取Java类方法updateTextFromNative
if(NULL != updateTextCallback){
env->CallVoidMethod(obj,updateTextCallback,flag);//调用Java方法
}
}
//error:
if(g_jvm->DetachCurrentThread() != JNI_OK){
LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
}
pthread_exit(0);
}
最后方便大家一起学习交流,附上完整的工程源码。
Java和C++相互调用:JniDemo