方法签名在jni的使用中经常都会用到,在java中会有重载,那么定位到一个方法的方式:类+方法名称+方法签名,那么我们先学习下签名规则:
咱们基本类型有各自的签名,如下表
类型名 | 签名 |
---|---|
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | J |
float | F |
double | D |
void | V |
看表就能知道,大多数基本类型的前面其实就是首字母的大写,有两个特殊的,boolean的签名是‘Z’,long的签名是‘J’
前面说到了基本类型,再来看看类的签名,比如:
类 | 全路径名 | 签名 |
---|---|---|
String | java.lang.String | Ljava/lang/String; |
Bundle | android.os.Bundle | Landroid/os/Bundle; |
Integer | java.lang.Integer | Ljava/lang/Integer; |
List | java.util.List | Ljava/util/List; |
我想看看上面,大家已经知道了他的规则,那就是:
L+全路径名(.需要改为/)+;
PS:对于list,它的签名依旧是list的类签名,对于范型值没有在签名中,大家可以试试,两个方法重载如果只改范型参数编译器是会报错的哦。
前面说了类的签名,咱们数组中的装载物也是类,那它的签名又是怎样呢?,再看看:
类 | 全路径名 | 签名 |
---|---|---|
String[] | java.lang.String | [Ljava/lang/String; |
Integer[] | java.lang.Integer | [Ljava/lang/Integer; |
Integer[][] | java.lang.Integer | [[Ljava/lang/Integer; |
看看上面表格,我想大家也看出来它的规则了:
[+类路径名。//对于前面的[,是几维数组就有几个[
ndkdemo xin$ javap -s ./MainActivity.class
Compiled from "MainActivity.java"
public class shixin.ndkdemo.MainActivity extends android.support.v7.app.AppCompatActivity {
public shixin.ndkdemo.MainActivity();
descriptor: ()V
protected void onCreate(android.os.Bundle);
descriptor: (Landroid/os/Bundle;)V
public native java.lang.String stringFromJNI();
descriptor: ()Ljava/lang/String;
public native int intFromJni();
descriptor: ()I
static {};
descriptor: ()V
}
看了这个输出,我想大家知道了方法签名的表述了
名称不说了,对于描述,可以看到,包含了参数与返回。规则:
(参数签名)返回签名
知道了步骤,那么试试实现:
/**
* 静态方法测试类
*/
public class StaticClassTest {
private static final String TAG = StaticClassTest.class.getSimpleName();
/**
* 名称,用于测试被jni修改
*/
private static int age = 5;
/**
* 调用静态方法测试
*/
public static void staticMethodTest() {
Log.d(TAG, "1 调用到静态方法了");
Log.d(TAG, "2 调用到静态方法了,age:" + age);
}
/**
* 调用带参数静态方法测试
*/
public static int staticMethodTest(String name) {
Log.d(TAG, "3 调用到带参静态方法了,name:" + name);
Log.d(TAG, "4 调用到带参静态方法了,修改后age:" + age);
return 1;
}
}
在前面基本步骤的第二步呢,我们会使用到方法的签名,前面说了,用于区分方法参数等,可以看作是java中的区分重载的体现。那么先来获取下方法的签名:
MBP:static_test shixin$ javap -s StaticClassTest.class
Compiled from "StaticClassTest.java"
public class shixin.ndkdemo.static_test.StaticClassTest {
public shixin.ndkdemo.static_test.StaticClassTest();
descriptor: ()V
public static void staticMethodTest();
descriptor: ()V
public static int staticMethodTest(java.lang.String);
descriptor: (Ljava/lang/String;)I
static {};
descriptor: ()V
}
这样就得到了签名分别是:
/**
* 测试静态方法调用
*/
extern "C"
JNIEXPORT void JNICALL
Java_shixin_ndkdemo_MainActivity_testStaticMethodUse(JNIEnv *env, jobject instance) {
//1. 使用FindClass方法找到类,参数:全路径
jclass class_static = env->FindClass("shixin/ndkdemo/static_test/StaticClassTest");
if (class_static == NULL) {
LOGE("没找到StaticClassTest");
return;
}
//2. 找到方法,无参数,无返回。参数:class、方法名称、方法签名
jmethodID jme_staticMethodTest = env->GetStaticMethodID(class_static, "staticMethodTest",
"()V");
if (jme_staticMethodTest == NULL) {
LOGE("没找到jme_staticMethodTest");
return;
}
//3. 开始调用,因为是返回void的,所以调用CallStaticVoidMethod
env->CallStaticVoidMethod(class_static, jme_staticMethodTest);
//带参数和返回值的静态方法
jmethodID jme_staticMethodTest_Have_Back = env->GetStaticMethodID(class_static,
"staticMethodTest",
"(Ljava/lang/String;)I");
if (jme_staticMethodTest_Have_Back == NULL) {
LOGE("没找到jme_staticMethodTest_Have_Back");
return;
}
//4. 在调用带参数方法前,我们试着修改下fild,即静态类中的age
jfieldID jfieldID_age = env->GetStaticFieldID(class_static, "age", "I");
if(jfieldID_age==NULL){
LOGE("没找到age字段");
return;
}
//给age字段赋值为7
env->SetStaticIntField(class_static,jfieldID_age,7);
jstring string_para = env->NewStringUTF("北鼻");
int a = env->CallStaticIntMethod(class_static, jme_staticMethodTest_Have_Back, string_para);
LOGE("调用后返回: %d", a);
//使用完,释放本地变量,这里有两个,class和后面用到的string字符串
env->DeleteLocalRef(class_static);
env->DeleteLocalRef(string_para);
}
查看注释就清楚步骤了。
D/StaticClassTest: 1 调用到静态方法了
D/StaticClassTest: 2 调用到静态方法了,age:5
D/StaticClassTest: 3 调用到带参静态方法了,name:北鼻
D/StaticClassTest: 4 调用到带参静态方法了,修改后age:7
I/Native-lib: 调用后返回: 1