注:编码工具是Android Studio。
目录
创建Android Studio编写JNI代码环境
File -> New Project -> Native C++。
JNI是什么?
JNI是JDK提供的一种能够让Java和Native之间互相操作的机制。
javah命令生成native方法签名
在Java中用native定义本地方法,具体的实现用C/C++实现,Java中可以直接调用native修饰的方法,调用它相当于调用了C/C++实现的方法体。
JNI有一套固定的native方法签名格式,可以用JDK自带的javah命令生成对应的方法签名。
在MainActivity中定义一个native方法,getStringFromNative,返回值为String,没有形式参数。
package com.wcc.jni.study;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
// 加载本地库
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Example of a call to a native method
TextView tv = findViewById(R.id.sample_text);
tv.setText(getStringFromNative());
}
// 本地方法在Java中只有声明,没有方法体
private native String getStringFromNative();
}
在命令行运行javah命令
D:\Android\AndroidStudioProjects\JniStudy\app\src\main\java>javah com.android.jni.study.MainActivity
会在java目录下生成一个.h格式的头文件,里面有native的方法签名。
com_android_jni_study_MainActivity.h
内容
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_android_jni_study_MainActivity */
#ifndef _Included_com_android_jni_study_MainActivity
#define _Included_com_android_jni_study_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_android_jni_study_MainActivity
* Method: getStringFromNative
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_android_jni_study_MainActivity_getStringFromNative
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
将头文件复制到cpp目录下。
在cpp文件中,引入头文件,并实现方法。
#include <jni.h>
#include "com_android_jni_study_MainActivity.h"
extern "C" // 必须,所有代码以C形式运行
JNIEXPORT
jstring // 对应Java的String
JNICALL// 可以省略
Java_com_android_jni_study_MainActivity_getStringFromNative
(JNIEnv *env, // JNI的核心,Java和Native的互相操作都靠env
jobject jobj// Java中的native方法声明在哪个类,就传哪个类的对象
) {
std::string hi = "hi from native";
return env->NewStringUTF(hi.c_str());
}
运行结果:
案例一:Native修改Java类属性值
MainActivity代码
package com.android.jni.study;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
// 加载本地库
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
updateText();
// Example of a call to a native method
TextView tv = findViewById(R.id.sample_text);
tv.setText(text);
}
private String text = "default";
private native void updateText();
}
Native代码
#include <jni.h>
#include <string>
extern "C"
JNIEXPORT void JNICALL
Java_com_android_jni_study_MainActivity_updateText(JNIEnv *env, jobject thiz) {
// 获取jobject对应的jclass
jclass jclz = env->GetObjectClass(thiz);
// 获取Java类属性FieldID
jfieldID f_id = env->GetFieldID(
jclz,
"text",// Java类属性名
"Ljava/lang/String;"// Java类属性类型签名
);
// 创建jstring
jstring jstr = env->NewStringUTF("NewValue");
// 通过FieldID修改Java类属性值
env->SetObjectField(thiz, f_id, jstr);
}
运行结果
案例二:Native调用Java类方法
MainActivity代码
package com.android.jni.study;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
// 加载本地库
static {
System.loadLibrary("native-lib");
}
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = findViewById(R.id.sample_text);
}
public native void callJavaMethod();
private void print(){
Log.e("Main", "Call From Native");
}
public void click(View view) {
callJavaMethod();
}
}
Native代码
#include <jni.h>
#include <string>
extern "C"
JNIEXPORT void JNICALL
Java_com_android_jni_study_MainActivity_updateText(JNIEnv *env, jobject thiz) {
// 获取jobject对应的jclass
jclass jclz = env->GetObjectClass(thiz);
// 获取Java类属性FieldID
jfieldID f_id = env->GetFieldID(
jclz,
"text",// Java类属性名
"Ljava/lang/String;"// Java类属性类型签名
);
// 创建jstring
jstring jstr = env->NewStringUTF("NewValue");
// 通过FieldID修改Java类属性值
env->SetObjectField(thiz, f_id, jstr);
}extern "C"
JNIEXPORT void JNICALL
Java_com_android_jni_study_MainActivity_callJavaMethod(JNIEnv *env, jobject thiz) {
// 获取jobject的jclass
jclass jclz = env->GetObjectClass(thiz);
// 获取methodID
jmethodID m_id = env->GetMethodID(
jclz,
"print",// 方法名
"()V"// 方法签名
);
// 通过methodID调用Java类方法
env->CallVoidMethod(
thiz,
m_id
);
}
运行结果
Java各种数据类型的签名
boolean --- Z
char --- C
byte --- B
short --- S
int --- I
long --- J
float --- F
double --- D
void --- V
引用类型 --- L全类名; 其中.用/代替,如:String的签名Ljava/lang/String;
数组 --- [元素签名,如:int[]的签名[I
可以用JDK的javap命令查看某个类中的方法和属性的签名,如:javap -s -p MainActivity,其中MainActivity是Class文件。
上述命令的输出格式如下:
public class com.android.jni.study.MainActivity extends androidx.appcompat.app.AppCompatActivity {
private android.widget.TextView tv;
descriptor: Landroid/widget/TextView;
public com.android.jni.study.MainActivity();
descriptor: ()V
protected void onCreate(android.os.Bundle);
descriptor: (Landroid/os/Bundle;)V
public native void callJavaMethod();
descriptor: ()V
private void print();
descriptor: ()V
public void click(android.view.View);
descriptor: (Landroid/view/View;)V
static {};
descriptor: ()V
}