关于so文件的编写
编译So文件的过程文章:https://blog.csdn.net/carson_ho/article/details/73250163
原理:利用 RegisterNatives 方法来注册 java 方法与 JNI 函数的一一对应关系
这里实现一个计算功能的代码!
实现流程:
1、利用结构体 JNINativeMethod 数组记录 java 方法与 JNI 函数的对应关系
JNINativeMethod的结构体:
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
第一个变量name是Java中函数的名字
第二个变量signature是用来字符串是描述了函数的参数和返回值
第三个变量fnPtr是函数指针 指向C函数
第二个signature参数需要讲下,因为我在写这篇文章的时候不懂(这些也会在smali中看到)
举个例(实际上这些字符是与函数的参数类型一一对应的):
"()V" "()" 中的字符表示参数,后面的则代表返回值。例如"()V" 就表示void Func();
"(II)V" 那么"(II)V" 表示 void Func(int, int);
"(Ljava/lang/String;Ljava/lang/String;)V" 那么就表示 void Func(String, String),前面的 L表示类的实例
具体的对应关系:
字符 Java类型 C类型
V void void
Z jboolean boolean
I jint int
J jlong long
D jdouble double
F jfloat float
B jbyte byte
C jchar char
S jshort short
数组则以"["开始,用两个字符表示
[I jintArray int[]
[F jfloatArray float[]
[B jbyteArray byte[]
[C jcharArray char[]
[S jshortArray short[]
[D jdoubleArray double[]
[J jlongArray long[]
[Z jbooleanArray boolean[]
如果JAVA函数位于一个嵌入类,则用$作为类名间的分隔符
例如 "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z"
结尾返回值为布尔值
这里注册的有加减乘除4个函数,如下实现:
JNINativeMethod nativeMethod[] = {
//java层的方法名;Java层的签名;指针指向C的函数
{"addTest","(FF)F",(void*)addTest}, // 那么这里的意思 参数两个float类型 返回值为float
{"subTest","(FF)F",(void*)subTest},
{"mulTest","(FF)F",(void*)mulTest},
{"divTest","(FF)F",(void*)divTest}
};
2、实现 JNI_OnLoad 方法,在加载动态库后,执行动态注册
JNIEXPORT jint JNI_OnLoad(JavaVm* vm, void* reserved)
个人理解:可以理解为DllMain入口, 在System.loadLibrary(xxx)函数时被调用
3、调用 FindClass 方法,获取 java 对象
jclass _jclass = (*env)->FindClass(env, "com/example/myjnitest/MainActivity");
4、调用 RegisterNatives 方法,传入 java 对象,以及 JNINativeMethod 数组,以及注册数目完成注册
if((*env)->RegisterNatives(env, _jclass, nativeMethod, sizeof(nativeMethod)/sizeof(nativeMethod[0]))!=JNI_OK){
return JNI_ERR;
}
坑点:jni注册的方法,一定要在java层进行声明,否则会报错,这尼玛巨坑。。。
计算器的实现:
.c文件:
#include <com_example_myjnitest_MainActivity.h>
jfloat addC(JNIEnv* env, jobject obj,jfloat a, jfloat b){
return a+b;
}
jfloat subC(JNIEnv* env,jobject obj,jfloat a,jfloat b){
return a-b;
}
jfloat mulC(JNIEnv* env,jobject obj,jfloat a,jfloat b){
return a*b;
}
jfloat divC(JNIEnv* env,jobject obj,jfloat a,jfloat b){
return a/b;
}
static JNINativeMethod nativeMethod[] = {
//java层的方法名;Java层的签名;指针指向C的函数
{"addTest","(FF)F",(void*)addC},// 那么这里的意思 参数两个float类型 返回值为float
{"subTest","(FF)F",(void*)subC},
{"mulTest","(FF)F",(void*)mulC},
{"divTest","(FF)F",(void*)divC}
};
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){ // 可以直接理解为dll的 dllmain 在System.loadLibrary(xxx)函数时被调用
JNIEnv* env;
//从JavaVM获取JNIEnv,一般使用1.4的版本,若未提供JNI_OnLoad()函数,VM会默认该使用最老的JNI 1.1版
if((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_4)!=JNI_OK){
return JNI_ERR;
}
//在MainActivity中进行注册Native数组方法
jclass _jclass = (*env)->FindClass(env, "com/example/myjnitest/MainActivity");
if((*env)->RegisterNatives(env, _jclass, nativeMethod, sizeof(nativeMethod)/sizeof(nativeMethod[0]))!=JNI_OK){
return JNI_ERR;
}
return JNI_VERSION_1_4; //必须返回该值
}
头文件
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_myjnitest_MainActivity */
#ifndef _Included_com_example_myjnitest_MainActivity
#define _Included_com_example_myjnitest_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_myjnitest_MainActivity
* Method: getFirstJniText
* Signature: ()Ljava/lang/String;
*/
jfloat addTest(JNIEnv* env,jobject obj,jfloat a,jfloat b);
jfloat subTest(JNIEnv* env,jobject obj,jfloat a,jfloat b);
jfloat mulTest(JNIEnv* env,jobject obj,jfloat a,jfloat b);
jfloat divTest(JNIEnv* env,jobject obj,jfloat a,jfloat b);
#ifdef __cplusplus
}
#endif
#endif
MainActivity:
package com.example.myjnitest;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity {
static{
System.loadLibrary("com_example_myjnitest_MainActivity");
}
public native float addTest(float a, float b); // 声明native方法
public native float subTest(float a, float b); // 声明native方法
public native float mulTest(float a, float b); // 声明native方法
public native float divTest(float a, float b); // 声明native方法
Button btn01;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn01 = (Button) findViewById(R.id.button1);
btn01.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
float a = addTest(Float.parseFloat("1.1".toString()), Float.parseFloat("2.2".toString()));
Toast.makeText(getApplicationContext(), "" + a, Toast.LENGTH_SHORT).show();
}
});
}
}