/*常用引用数据类型对比
* All object jobject
* java.lang.Class jclass
* java.lang.String string
* int[] jintArray
* Object[] jobjectArray
*
* JNI 函数签名信息
* java支持函数重载,因此仅仅根据函数名是没法找到对应的JNI函数。
* 为了解决这个问题,JNI将参数类型和返回值类型作为函数的签名信息
*
* JNI规范定义的函数签名信息格式: (参数1类型字符…)返回值类型字符
*
* 例子:Java函数 对应的 函数签名
* String sayHello2(); "()Ljava/lang/String;"
* long fun(int i,Class c) "(ILjava/lang/Class;)J"
* void fun(byte[] bytes) "([B)V"
*
* JNI常用的数据类型及对应字符:
*
* Java类型 对应字符
* void V
* boolean Z
* int I
* long J
* double D
* float F
* byte B
* char C
* short S
* int[] [i
* String Ljava/lang/String;(后面一定要加;)
* Object[] [Ljava/lang/object;
*
*
* JNIEnv 是个啥? 它是一个线程相关的结构体, 该结构体代表了 Java 在本线程的运行环境。通过JNIEnv可以调用到一系列JNI系统函数,
* 用于访问Java成员变量何成员方法,调用Java构造方法创建Java对象等
*
* JNIEnv线程相关性: 每个线程中都有一个 JNIEnv 指针。JNIEnv只在其所在线程有效, 它不能在线程之间进行传递
*
* */
MainActivity.java
public class MainActivity extends Activity {
// Used to load the 'myapplication' library on application startup.
static {
System.loadLibrary("myapplication");
}
private ActivityMainBinding binding;
private int code = 8;
private String msg = "hello JNI";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
// Example of a call to a native method
TextView tv = binding.sampleText;
//java 调用 Native函数
tv.setText(stringFromJNI());
tv.setText(sayHello());
tv.setText(sayHello2());
tv.setText(sayAloveB(5,2)+"");
testCallJava(MainActivity.this);
}
public void cCallJava(String str){
Log.i("TAG","cCallJava:" +str);
Toast.makeText(this,str,Toast.LENGTH_SHORT).show();
}
public native void testCallJava(MainActivity activity);
/**
* A native method that is implemented by the 'myapplication' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
public native String sayHello();
public native String sayHello2();
public native int sayAloveB(int a,int b);
}
native-lib.cpp
#include <jni.h>
#include <string>
#include "android/log.h"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,"tww" ,__VA_ARGS__)
extern "C" JNIEXPORT jstring JNICALL
Java_com_autochips_myapplication_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_autochips_myapplication_MainActivity_sayAloveB(JNIEnv *env, jobject thiz, jint a, jint b) {
// TODO: implement sayAloveB()
jint ab = a + b;
return ab;
}
//静态注册
extern "C"
JNIEXPORT jstring JNICALL
Java_com_autochips_myapplication_MainActivity_sayHello(
JNIEnv *env, jobject /* this */) {
std::string hello = "tang say Hello JNI";
return env->NewStringUTF(hello.c_str());
}
//动态注册
jstring sayHello2(JNIEnv *env, jobject /* this */) {
std::string hello = "tang sayHello2 ----------";
//获得一维数组的类引用,即jintArray类型
jclass intArrayClass = env->FindClass("[I");
//构造一个指向jintArray类一维数组的对象数组,该对象数组初始大小为length,类型为 jsize
jobjectArray jobjectArray1 = env->NewObjectArray(6, intArrayClass, NULL);
return env->NewStringUTF(hello.c_str());
}
static const JNINativeMethod gMethods[] = {
{"sayHello2",//对应java交互类的名字
"()Ljava/lang/String;",//对应方法名的函数签名
(jstring *) sayHello2}//对应 cpp交互类的指针函数
};
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
__android_log_print(ANDROID_LOG_INFO, "native", "Jni_OnLoad");
JNIEnv *env = NULL;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK)//从JavaVM获取JNIEnv,一般使用1.4的版本
return -1;
//注意:这里 FindClass 必须要和交互类的 包名对应上,并换成[/]符号
jclass clazz = env->FindClass("com/autochips/myapplication/MainActivity");
if (!clazz) {
__android_log_print(ANDROID_LOG_INFO, "native", "cannot get class,"
"com/autochips/myapplication/MainActivity");
return -1;
}
if (env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0]))) {
__android_log_print(ANDROID_LOG_INFO, "native", "register native method failed!\n");
return -1;
}
return JNI_VERSION_1_4;
}
//native函数调用Java函数
extern "C"
JNIEXPORT void JNICALL
Java_com_autochips_myapplication_MainActivity_testCallJava(JNIEnv *env, jobject thiz,
jobject activity) {
// TODO: implement testCallJava()
//获取MainActivity
jclass cls = env->GetObjectClass(activity);
//拿成员变量ID
jfieldID codeId = env->GetFieldID(cls,"code","I");
jfieldID msgId = env->GetFieldID(cls,"msg","Ljava/lang/String;");
//通过id获取其值
jint code = env->GetIntField(activity,codeId);
jstring msg = (jstring)env->GetObjectField(activity,msgId);
//获取java.lang.String对象中的内容
const char *cMsg = env->GetStringUTFChars(msg,JNI_FALSE);//C++里面没有
LOGI("code = %d,msg = %s",code,cMsg);
env->ReleaseStringUTFChars(msg,cMsg);//用完String之后释放
//找到函数ID
jmethodID callJavaMethodId = env->GetMethodID(cls,"cCallJava","(Ljava/lang/String;)V");
jstring nativeMsg = env->NewStringUTF("java method cCallJava Go!!!");
//调用java中的cCallJava函数
env->CallVoidMethod(activity,callJavaMethodId,nativeMsg);
// 这里的DeleteLocalRef可以不执行,在函数执行完毕后LocalRef会自动释放,
// 但是在循环次数较多的循环中需要Delete,否则可能会溢出
env->DeleteLocalRef(msg);
env->DeleteLocalRef(nativeMsg);
env->DeleteLocalRef(cls);
}
JNI 基础
native函数调用Java函数
阿豪JNI