熟练javah 、javap命令
配置生成dll方法
如果你不想这么麻烦每一次都去配置环境变量,那么你就固定配置一个环境变量目录
之后的这些dll动态库,拷贝下去就好了
原始写法
JNIEXPORT jstring JNICALL Java_dllProject_DllMain_getTextString(JNIEnv *env, jobject jobj){
return (*env)->NewStringUTF(env, "Hello Jni!");
}
#define _CRT_SECURE_NO_WARNINGS
//注意:导入系统的头文件<>,导入自己头文件""
#include
#include
#include
#include "dllProject_DllMain.h"
JNIEXPORT jstring JNICALL Java_dllProject_DllMain_getTextString
(JNIEnv *env, jobject jobj){
return (*env)->NewStringUTF(env, "Hello Jni!");
}
JNIEXPORT jstring JNICALL Java_dllProject_DllMain_updateName
(JNIEnv *env, jobject jobj){
//在C中不能够通过实例调用属性(说白了就是反射)
//class对象(类对象)
jclass cls = (*env)->GetObjectClass(env, jobj);
//获取属性(java中称之为属性对象)
//参数一:JNIEnv *env
//参数二:class对象
//参数三:属性名称
//参数四:属性签名(说白了就是去指定类型)
//属性签名就是类型的简写
jfieldID fid = (*env)->GetFieldID(env, cls, "name", "Ljava/lang/String;");
//获取属性的值(类似java反射中)
//注意:Get<签名类型>Field
//参数一:JNIEnv *env
//参数二:哪一个对象--jobj
//参数三:哪一个属性--fid
//返回值:就是我们的jstring(在java中所有的类都是object子类)
//在jni中也是,所有的都是jobject的子类
jstring jstr = (*env)->GetObjectField(env, jobj, fid);
//将jni的类型转成C/C++中的数据类型
char* str = (*env)->GetStringUTFChars(env, jstr, NULL);
//更新str
char newStr[] = " Hello jni";
strcat(str, newStr);
//又要将C/C++数据类型转成jni类型
jstr = (*env)->NewStringUTF(env, str);
//更新java对象中的属性的值
(*env)->SetObjectField(env, jobj, fid, jstr);
return jstr;
}
JNIEXPORT void JNICALL Java_dllProject_DllMain_updateAge
(JNIEnv *env, jobject jobj){
//在C中不能够通过实例调用属性(说白了就是反射)
//class对象(类对象)
jclass cls = (*env)->GetObjectClass(env, jobj);
//获取属性
jfieldID fid = (*env)->GetStaticFieldID(env, cls, "age", "I");
//获取值
//注意:GetStatic<类型>Field
jint age = (*env)->GetStaticIntField(env, cls, fid);
//jint就是long类型,可以直接操作
age += 50;
//更新java静态属性值
//注意:SetStatic<类型>Field
(*env)->SetStaticIntField(env, cls, fid, age);
}
//小案例:生成随机数
JNIEXPORT jint JNICALL Java_dllProject_DllMain_getNumber
(JNIEnv *env, jobject jobj){
//在C中不能够通过实例调用属性(说白了就是反射)
//class对象(类对象)
jclass cls = (*env)->GetObjectClass(env, jobj);
//获取实例方法
//javap -s -p 类名
jmethodID mid = (*env)->GetMethodID(env, cls, "getRandomInt", "(I)I");
//调用方法(执行方法)类似于java中的invoke
//参数一:env
//参数二:实例对象
//参数三:调用哪一个方法
//参数四:传入的参数列表
jint randomInt = (*env)->CallIntMethod(env, jobj, mid, 100);
return randomInt;
}
//javap命令,能够获取签名
JNIEXPORT void JNICALL Java_dllProject_DllMain_executeRandomUUID
(JNIEnv *env, jobject jobj){
//在C中不能够通过实例调用属性(说白了就是反射)
//class对象(类对象) Class> clz
jclass cls = (*env)->GetObjectClass(env, jobj);
//获取静态方法
jmethodID mid = (*env)->GetStaticMethodID(env, cls, "getUUID", "()Ljava/lang/String;");
//执行方法
jstring jstr = (*env)->CallStaticObjectMethod(env, cls, mid);
//将jstring转成c的字符串
char* uuid = (*env)->GetStringUTFChars(env, jstr, NULL);
//可以将值写到一个文件中
char path[100];
sprintf(path, "F://%s.txt", uuid);
//将uuid写入文件中
FILE*f = fopen(path, "w");
fputs(uuid, f);
fclose(f);
}
需要返回包的根目录,并执行如下命令,生成头文件。 javah -d …/jni dllProject.DllMain
public class DllMain {
public String name = "Zero";
public static int age = 18;
public native String getTextString();
public native String updateName();
public native void updateAge();
public native int getNumber();
public native void executeRandomUUID();
public static void main(String[] args) {
DllMain test = new DllMain();
System.out.println(test.getTextString());
// 一旦调用了该方法,在C中更新name
test.updateName();
System.out.println("更新后的值:" + test.name);
test.updateAge();
System.out.println("更新后的age值:" + age);
System.out.println("得到一个数:" + test.getNumber());
test.executeRandomUUID();
}
/**
* 生成一个随机数
*
* @param max
* @return
*/
public int getRandomInt(int max) {
Random rd = new Random();
return rd.nextInt(max);
}
/**
* 生成一个随机字符串
*
* @return
*/
public static String getUUID() {
return UUID.randomUUID().toString();
}
// 加载库
static {
// 加载动态库
// Windows系统下:.dll
// Linux环境下:.so(Android)
// 加载dll动态库,不需要后缀(原名称:dllTest.dll)
System.loadLibrary("dllTest");
}
}
//1、在C语言中访问Java的构造方法
JNIEXPORT jobject JNICALL Java_dllProject_test2_JniTest_callCustructor
(JNIEnv *env, jobject jobj, jstring jpath){
//参数一:JNIEnv *env(包含了所有NDK相关函数)
//参数二:完整类名
jclass cls = (*env)->FindClass(env, "dllProject/test2/Company");
//创建一个对象(我要执行cls中的构造方法)
//获取构造方法
//:代表就是调用构造方法(固定写法)
//()V:无参数构造方法签名
jmethodID init_mid = (*env)->GetMethodID(env, cls, "", "()V");
//根据构造方法创建对象
jobject company_obj = (*env)->NewObject(env, cls, init_mid);
//调用Java Company类中的salary()--发工资方法
//获取salary()方法对象(对象方法)
jmethodID salary_mid = (*env)->GetMethodID(env, cls, "salary", "()V");
//执行该方法
(*env)->CallVoidMethod(env, company_obj, salary_mid, NULL);
//获取当前时间戳作为文件的名称,然后写一句话
//首先获取getTime方法
jmethodID gettime_mid = (*env)->GetMethodID(env, cls, "getTime", "()Ljava/lang/String;");
//执行方法(java的string)
jstring jtime_str = (*env)->CallObjectMethod(env, company_obj, gettime_mid, NULL);
//将String转成C中的认识的字符数组
const char *time_str = (*env)->GetStringUTFChars(env, jtime_str, NULL);
//获取SD卡路径
const char *path_str = (*env)->GetStringUTFChars(env, jpath, NULL);
char file_path[100] = { 0 };
//在路径中拼接文件名称
strcpy(file_path, path_str);
strcat(file_path, "/log.txt");
//strintf(file_path,"/%c.txt",time_str);
//写入文件中
FILE *fp = fopen(file_path, "w");
fputs("今天我不高兴,你的礼物我不收了!", fp);
fclose(fp);
return company_obj;
}
//打印日志
void customerPrintfLog(JNIEnv *env, const char *path_str, const char *content){
//获取SD卡路径
char file_path[100] = { 0 };
//在路径中拼接文件名称
strcpy(file_path, path_str);
strcat(file_path, "/log.txt");
//strintf(file_path,"/%c.txt",time_str);
//写入文件中
FILE *fp = fopen(file_path, "w");
fputs(content, fp);
fclose(fp);
}
//C中访问Java的父类方法
//首先访问子类方法,然后在访问父类方法
JNIEXPORT void JNICALL Java_dllProject_test2_JniTest_callSuperMethod
(JNIEnv *env, jobject jobj, jstring jpath){
const char *path_str = (*env)->GetStringUTFChars(env, jpath, NULL);
//首先获取company属性
//cls->NDKTest类对象
jclass cls = (*env)->GetObjectClass(env, jobj);
jfieldID fid = (*env)->GetFieldID(env, cls, "company", "LdllProject/test2/Company;");
//获取属性的值(company_obj = new GoogleCompany())
jobject company_obj = (*env)->GetObjectField(env, jobj, fid);
//Company对应的类对象(GoogleCompany.class)
jclass company_cls = (*env)->GetObjectClass(env, company_obj);
jclass super_cls = (*env)->GetSuperclass(env, company_cls);
//调用company_obj上salary方法
jmethodID salary_mid = (*env)->GetMethodID(env, super_cls, "salary", "()V");
//执行方法
//注意:这个地方你执行的是方法(CallVoidMethod,而不是父类方法
//CallNonvirtualVoidMethod:执行父类对象方法
//CallVoidMethod:执行当前对象的方法
(*env)->CallVoidMethod(env, company_obj, salary_mid, NULL);
(*env)->CallNonvirtualVoidMethod(env, company_obj, super_cls, salary_mid, NULL);
customerPrintfLog(env, path_str, "5");
}
JNIEXPORT jstring JNICALL Java_dllProject_test2_JniTest_getCNChars
(JNIEnv *env, jobject jobj, jstring jpath, jstring text){
/*//直接将文本输出到文件中
const char *str = (*env)->GetStringUTFChars(env, text, NULL);
const char *path = (*env)->GetStringUTFChars(env, jpath, NULL);
customerPrintfLog(env, path, str);
//不同的环境编译生成动态库编码不一样
//Windows环境编译:GB2312(生成的是dll动态库)
//Window下注意:项目编码要是UTF-8、GBK编码,NDK中返回的编码GB2312
//Mac os(Linux环境):支持中文(直接开发,不需要转),如果你要进行编码
//那么编码要和项目编码一致,要不然乱码
return (*env)->NewStringUTF(env, "我不是富二代,至少要成为富二代他爹");
*/
char *cstr = "我说中文";
jclass str_cls = (*env)->FindClass(env, "java/lang/String");
jmethodID constructor_mid = (*env)->GetMethodID(env, str_cls, "", "([BLjava/lang/String;)V");
//char * -> char[] ->jbyteArray -> jbyte *
jbyteArray bytes = (*env)->NewByteArray(env, strlen(cstr));
//bytes数组赋值
(*env)->SetByteArrayRegion(env, bytes, 0, strlen(cstr), cstr);
//jstring charsetName = (*env)->NewStringUTF(env, "UTF-8");
jstring charsetName = (*env)->NewStringUTF(env, "GB2312");
//返回GB2312中文编码jstring
return (*env)->NewObject(env, str_cls, constructor_mid, bytes, charsetName);
}
public class Company {
private String companyName;
public Company() {
System.out.println("调用了构造方法......");
}
public Company(String companyName) {
this.companyName = companyName;
}
public void salary() {
System.out.println("公司发工资了!我很高兴,又可以给妹子买礼物");
}
public String getTime() {
return String.valueOf(System.currentTimeMillis());
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
}
public class GoogleCompany extends Company {
@Override
public void salary() {
System.out.println("Google公司发工资......");
}
}
public class JniTest {
// C访问Java构造方法(通过C创建Company对象)
public native Company callCustructor(String path);
// 向SD卡写日志信息
public native void callSuperMethod(String path);
// 中文乱码问题分析
public native String getCNChars(String path, String text);
private Company company = new GoogleCompany();
static {
System.loadLibrary("dllTest");
}
}