转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/119209444
本文出自【赵彦军的博客】
文章目录
Java 类型和JNI符号对比
对应基础类型字段的转换:
Java 类型 | JNI符号 |
---|---|
Boolean | Z |
Byte | B |
Char | C |
Short | S |
Int | I |
Long | J |
Float | F |
Double | D |
对于引用类型的字段签名转换,是大写字母 L 开头,然后是类的签名转换,最后以 ; 结尾。
Java 类型 | JNI符号 |
---|---|
String | Ljava/lang/String; |
Class | Ljava/lang/Class; |
Throwable | Ljava/lang/Throwable; |
int[] | [I |
Object[] | [Ljava/lang/Object; |
对于方法签名描述的转换,首先是将方法内所有参数转换成对应的字段描述,并全部写在小括号内,然后在小括号外再紧跟方法的返回值类型描述。
Java 类型 | JNI 对应的描述转换 |
---|---|
String f(); | ()Ljava/lang/String; |
long f(int i, Class c); | (ILjava/lang/Class;)J |
String(byte[] bytes); | ([B)V |
这里要注意的是在 JNI 对应的描述转换中不要出现空格。
JNI 访问属性
创建 Util.java 类
/**
* @author : zhaoyanjun
* @time : 2021/7/29
* @desc :
*/
public class Util {
String usernName = "zhaoyanjun";
int age = 10;
static float key = 2f;
//修改类string属性
native void changeNameValue();
//修改类int属性
native void changeAgeValue();
//改变静态变量值
native void changeStaticValue();
}
访问 String 属性、int 属性
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val util = Util()
util.changeNameValue() //修改string
util.changeAgeValue() //修改int
util.changeStaticValue() //修改静态变量
binding.sampleText.text = "value:${util.usernName} ${util.age} ${Util.key}"
}
companion object {
// Used to load the 'native-lib' library on application startup.
init {
System.loadLibrary("native-lib")
}
}
}
native-lib.cpp
//修改字符串属性
extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_Util_changeNameValue(JNIEnv *env, jobject thiz) {
//获取class
jclass cls = env->GetObjectClass(thiz);
//获取字段
jfieldID fid = env->GetFieldID(cls, "usernName", "Ljava/lang/String;");
//创建新值
jstring str = env->NewStringUTF("niu b");
//给字段赋值
env->SetObjectField(thiz, fid, str);
}
//修改int属性
extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_Util_changeAgeValue(JNIEnv *env, jobject thiz) {
//获取class
jclass cls = env->GetObjectClass(thiz);
//获取字段
jfieldID fid = env->GetFieldID(cls, "age", "I");
//给字段赋值
env->SetIntField(thiz, fid, 100);
}
//修改静态属性
extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_Util_changeStaticValue(JNIEnv *env, jobject thiz) {
//获取class
jclass cls = env->GetObjectClass(thiz);
//获取字段
jfieldID fid = env->GetStaticFieldID(cls, "key", "F");
//获取静态字段值
jfloat value = env->GetStaticFloatField(cls, fid);
//给字段赋值
env->SetStaticFloatField(cls, fid, value + 100);
}
运行起来,看看效果
值已经改变。
native方法,非晶态和静态区别
Util.java
public class Util {
native void fun1();
native static void fun2();
}
native-lib.cpp
//非静态方法
extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_Util_fun1(JNIEnv *env, jobject thiz) {
}
//静态方法
extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_Util_fun2(JNIEnv *env, jclass clazz) {
}
可以看到,非静态方法,参数是 jobject
; 静态方法,参数是jclass
。
多种方式获得 jclass
extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_Util_fun1(JNIEnv *env, jobject thiz) {
//方式一:FindClass,参数传入类全路径
jclass cls = env->FindClass("com/example/myapplication/Util");
//方式二:GetObjectClass
jclass cls2 = env->GetObjectClass(thiz);
}
JNI 方法Java方法
无参无返回值
/**
* @author : zhaoyanjun
* @time : 2021/7/29
* @desc :
*/
public class Util {
void run() {
Log.d("util-", "runing");
}
String speak(String message) {
Log.d("util-", "speak " + message);
return message + " java";
}
//无参无返回值
native void callRun();
}
native-lib.cpp
extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_Util_callRun(JNIEnv *env, jobject thiz) {
//获取jclass
jclass cls = env->GetObjectClass(thiz);
//获取方法
jmethodID method = env->GetMethodID(cls, "run", "()V");
//调用方法
env->CallVoidMethod(thiz, method);
}
无参符号:()
, 无返回值符号:V
, 结合起来就是 ()V
有参数有返回值
native String callSpeak();
native-lib.cpp
//调用带有
extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_myapplication_Util_callSpeak(JNIEnv *env, jobject thiz) {
//获取jclass
jclass cls = env->GetObjectClass(thiz);
//获取方法
jmethodID method = env->GetMethodID(cls, "speak", "(Ljava/lang/String;)Ljava/lang/String;");
jstring str = env->NewStringUTF("jni hello");
//方法调用,并接收返回值
jobject result = env->CallObjectMethod(thiz, method, str);
//返回结果
return static_cast<jstring>(result);
}
静态方法
方式1:非静态 native 方法
public class Util {
static void run() {
Log.d("util-", "runing");
}
native void callRun();
}
native-lib.cpp
extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_Util_callRun(JNIEnv *env, jobject thiz) {
jclass cls = env->GetObjectClass(thiz);
jmethodID method = env->GetStaticMethodID(cls, "run", "()V");
env->CallStaticVoidMethod(cls, method);
}
调用:
val util = Util()
util.callRun()
需要注意的是
- 获取静态方法用
GetStaticMethodID
- 调用静态方法用
CallStaticVoidMethod
方式2:静态 native 方法
public class Util {
static void run() {
Log.d("util-", "runing");
}
//静态的
native static void callRun();
}
native-lib.cpp
extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_Util_callRun(JNIEnv *env, jclass clazz) {
jmethodID method = env->GetStaticMethodID(clazz, "run", "()V");
env->CallStaticVoidMethod(clazz, method);
}
调用:
Util.callRun()
需要注意的是:静态的 native
方法,必须用 类名调用。非静态的 nativie
方法,需要用对象调用。
实战演练: 调用Android Log
我们首先来看系统Log类
要确认两点信息
第一:类的包名是:android.util
第二:d() 方法,两个参数,都是 String 类型。返回值是 int
下面我们就可以写代码了。
首先创建 native 方法,有两个参数,一个是 tag , 一个是 message
public class Util {
//静态的
native static void logd(String tag, String message);
}
native-lib.cpp
extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_Util_logd(JNIEnv *env, jclass clazz, jstring tag, jstring message) {
//获取Log class
jclass cls = env->FindClass("android/util/Log");
//获取 d() 方法
jmethodID method = env->GetStaticMethodID(cls, "d",
"(Ljava/lang/String;Ljava/lang/String;)I");
//调用方法
env->CallStaticIntMethod(cls, method, tag, message);
}
具体使用:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//调用c++输出日志
Util.logd("china--", "good")
}
companion object {
// Used to load the 'native-lib' library on application startup.
init {
System.loadLibrary("native-lib")
}
}
}
实战演练: 弹 Toast
c 代码
/**
* 显示一个 Toast
* Toast.makeText(this,"message",Toast.LENGTH_SHORT).show();
*/
extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_Util_showToast(JNIEnv *env, jclass clazz, jobject context,
jstring message) {
//获取Toast
jclass cls = env->FindClass("android/widget/Toast");
//获取 makeText 方法
jmethodID makeText = env->GetStaticMethodID(cls, "makeText",
"(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;");
//调用makeText方法, Toast.LENGTH_SHORT=0
jobject toast = env->CallStaticObjectMethod(cls, makeText, context, message, 0);
//获取 show 方法
jmethodID show = env->GetMethodID(cls, "show", "()V");
//调用 show 方法
env->CallVoidMethod(toast, show);
}
java 方法声明
public class Util {
native static void showToast(Context context, String message);
}
方法调用:
//显示一个toast
Util.showToast(this, "今天是周一")
实战演练:数组传值
Util.java
类
public class Util {
native void run();
//这个方法时c调用
public void show(String[] array) {
for (int i = 0; i < array.length; i++) {
Log.d("show-", "" + i + " " + array[i]);
}
}
}
可以看到 Util 有两个方法,一个是 run , 一个是 show 方法,参数是一个 String 数组。
我们现在来做,java 调用 run
, 然后 c 实现的 run 再调用 show方法
,并且传值 。
c 代码如下:
extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_Util_run(JNIEnv *env, jobject thiz) {
jclass cls = env->GetObjectClass(thiz);
//获取show反复
jmethodID show = env->GetMethodID(cls, "show", "([Ljava/lang/String;)V");
//定义string数组
int size = 3;
jclass strClass = env->FindClass("java/lang/String");
jobjectArray resultArray = env->NewObjectArray(size, strClass, nullptr);
//string数组赋值
jstring strItem;
for (int i = 0; i < size; ++i) {
strItem = env->NewStringUTF("string in native");
env->SetObjectArrayElement(resultArray, i, strItem);
}
//调用 show 方法
env->CallVoidMethod(thiz, show, resultArray);
}
java 调用 run , 代码如下:
Util().run()
输出结果如下:
D/show-: 0 string in native
D/show-: 1 string in native
D/show-: 2 string in native