Android-JNI

socket

socket 为标socket协议, 同Windwos使用方法.

LinuxSocket多了一种使用方法, 因为socket都是可以映射文件
则可以做无需网络的socket(跨进程通讯使用socket,基于文件对象)
使用方法同正常的Socket

生成JNI头文件

  1. 类中声明native方法
  2. 在源码根目录 javah packetNmae.className生成JNI头文件

javah 程序
-classpath 目录(一般为当前目录)
-jni 包名.类名
javah -classpath. -jni packetNmae.className

获得方法签名

javap -p -s -classpath . packetName.className

反射调用

调用流程:

参数说明:
env: JAVA虚拟机环境
第一个jobj: 类的this指针
后边跟的参数1 ,2 ….

1
2
3
4
5
6
7
8
9
10
//通过RTTI, 使用字符串获得RunTimeClass
//packetNameStr中的'.'替换为'/'
jclass clazz = (*env)->FindClass(env, packetNameStr);

//通过方法名,方法签名(传参方式),获得方法ID
jmethodID methodID = (*env)->GetMethodID(env, clazz, FunName, FunSig);

//调用Void返回值的方法,
//虚拟机环境, 类对象, 方法ID, 方法参数1, 方法参数2 ....
(*env)->CallVoidMethod(env, obj, methodID, arg1);

SO中注册一个未导出的函数

修改函数的导出属性

1
2
3
//隐藏导出方法
__attribute__ ((visibility ("hidden"))) jstring JNICALL MyAdd
(JNIEnv *pEnv, jobject obj, jint n1, jint n2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

//隐藏全局字段
__attribute__ ((visibility ("hidden"))) JNINativeMethod g_NativeMethod[] = {
"MyAdd", "(II)Ljava/lang/String;", (void*)MyAdd
};

//SO的 JNI 回调, 相比_init 会传入两个参数
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
LOGD("JNI_OnLoad");

//获取jniEnv环境
JNIEnv *pEnv = NULL;
int version = vm->GetEnv((void**)&pEnv, JNI_VERSION_1_4);
LOGD("GetEnv:%d", version);
if (pEnv == NULL)
{
LOGD("GetEnv error");
return JNI_VERSION_1_4;
}

//获取成功
LOGD("JNI_OnLoad GetEnv=%p", pEnv);

//获取MainActivity的RunTimeClass
jclass clsMainActivity = pEnv->FindClass("cr/jni/MainActivity");
if (IsException(pEnv))
return JNI_VERSION_1_4;

//注册本地方法
pEnv->RegisterNatives(clsMainActivity, g_NativeMethod,
sizeof(g_NativeMethod) / sizeof(g_NativeMethod[0]));
if (IsException(pEnv))
return JNI_VERSION_1_4;

return JNI_VERSION_1_4;
}

直接在类中声明为Native方法,直接可以调用
因为JVM有保存全局的全部的方法表
结构如下:

1
2
3
4
5
typedef struct {
const char* name; //方法名
const char* signature; //方法签名(传参方式)
void* fnPtr; //方法指针
} JNINativeMethod;
文章目录
  1. 1. socket
  2. 2. 生成JNI头文件
  3. 3. 获得方法签名
  4. 4. 反射调用
  5. 5. SO中注册一个未导出的函数
|