今天我们继续学习jni相关知识点————调用java构造方法。
一. jni调用java类的构造方法
首先我们创建一个java类,并声明其无参构造和有参构造,并添加日志打印:
package com.carey.myndk;
import android.util.Log;
public class Company {
private String name;
public Company() {
Log.i("carey", "==== 调用了Company构造.");
}
public Company(String name) {
this.name = name;
Log.i("carey", "==== 调用了Company有参构造,name:" + name);
}
public void salary() {
Log.i("carey", "==== 公司发工资了,很开心。");
}
public String getName() {
return name;
}
}
同时我们新增native方法:
public native Company callConstructor();
并在cpp文件中进行方法的实现:
extern "C"
JNIEXPORT jobject JNICALL
Java_com_carey_myndk_MainActivity_callConstructor(JNIEnv *env, jobject thiz) {
// 通过类的全类名获取类对象
jclass cls = env->FindClass("com/carey/myndk/Company");
// 获取构造方法 参数1类对象 参数2 <init>代表调用构造方法 参数3 ()V 代表无参数构造方法签名
jmethodID initMid = env->GetMethodID(cls, "<init>", "()V"); // 无参构造
// 根据构造方法创建对象
jobject companyObj = env->NewObject(cls, initMid);
// 获取salary方法
jmethodID salaryMid = env->GetMethodID(cls, "salary", "()V");
// 调用company的salary方法
env->CallVoidMethod(companyObj, salaryMid);
return companyObj;
}
通过上面的方法我们能创建Company对象并返回,我们在java中调用callConstructor并增加打印:
Company company = callConstructor();
Log.i("carey", "==== callConstructor, 创建对象 " + company);
查看控制台日志打印:
我们可以看到,调用了Company的无参构造创建了对象,同时调用了对象的方法。
我们在app下build.gradle文件中配置了支持的CPU架构:
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}
项目编译成功后会在 debug环境下:app/build/intermediates/cmake/debug/obj release环境下:app/build/intermediates/cmake/release/obj 下看到生成的so文件:
二. jni调用java类的有参构造方法
定义native方法:
public native Company callParamsConstructor();
在cpp文件中实现,调用company的有参构造函数,并传入名称:
extern "C"
JNIEXPORT jobject JNICALL
Java_com_carey_myndk_MainActivity_callParamsConstructor(JNIEnv *env, jobject thiz) {
// 通过类的全类名获取类对象
jclass cls = env->FindClass("com/carey/myndk/Company");
// 获取构造方法
// 参数1类对象
// 参数2 <init>代表调用构造方法
// 参数3 (Ljava/lang/String;)V 代表有参数构造方法签名
jmethodID initMid = env->GetMethodID(cls, "<init>", "(Ljava/lang/String;)V"); // 有参构造
// 要传入的参数,公司名称
jstring name = env->NewStringUTF("MyCompany");
// 根据有参构造方法创建对象
jobject companyObj = env->NewObject(cls, initMid, name);
return companyObj;
}
在java中调用,并将公司名称显示在页面上:
Company company = callParamsConstructor();
tv.setText("公司名称:" + company.getName());
查看控制台日志输出:
页面结果显示:
三. jni调用java类成员方法,并将结果写入文件
首先在Company类中新增获取时间戳的方法:
public String getTime() {
return String.valueOf(System.currentTimeMillis());
}
创建native方法把内容写入文件中:
public native String writeContentToFile(String path);
同时在cpp文件中实现该方法:
extern "C"
JNIEXPORT jstring JNICALL
Java_com_carey_myndk_MainActivity_writeContentToFile(JNIEnv *env, jobject thiz, jstring path) {
// 通过类的全类名获取类对象
jclass cls = env->FindClass("com/carey/myndk/Company");
if (cls == NULL) {
return NULL; // 处理类查找失败
}
// 获取构造方法 参数1类对象 参数2 <init>代表调用构造方法 参数3 ()V 代表无参数构造方法签名
jmethodID initMid = env->GetMethodID(cls, "<init>", "()V"); // 无参构造
if (initMid == NULL) {
return NULL; // 处理方法查找失败
}
// 根据构造方法创建对象
jobject companyObj = env->NewObject(cls, initMid);
if (companyObj == NULL) {
return NULL; // 处理对象创建失败
}
jmethodID getTimeMid = env->GetMethodID(cls, "getTime", "()Ljava/lang/String;");
if (getTimeMid == NULL) {
return env->NewStringUTF("getTimeMid == NULL"); // 处理方法查找失败
}
jstring jTimeStr = (jstring)env->CallObjectMethod(companyObj, getTimeMid);
if (jTimeStr == NULL) {
return env->NewStringUTF("jTimeStr == NULL"); // 处理方法调用失败
}
// 将 String 转成 C 中字符数组
const char *time_str = env->GetStringUTFChars(jTimeStr, NULL);
if (time_str == NULL) {
return NULL; // 处理字符串转换失败
}
// 获取 SD 卡路径
const char *path_str = env->GetStringUTFChars(path, NULL);
if (path_str == NULL) {
env->ReleaseStringUTFChars(jTimeStr, time_str);
return NULL; // 处理字符串转换失败
}
char file_path[100] = {0};
// 路径上拼接文件名称
strncpy(file_path, path_str, sizeof(file_path) - 1);
strncat(file_path, "/time_log.txt", sizeof(file_path) - strlen(file_path) - 1);
// 存储拼接后的字符串
char content[200] = {0};
// 先将 time_str 内容复制到 content 中
strncpy(content, time_str, sizeof(content) - 1);
// 再将 "今天我很开心," 追加到 content 后面
strncat(content, "今天我很开心,", sizeof(content) - strlen(content) - 1);
// 将 time 时间戳内容写入到文件中
FILE *fp = fopen(file_path, "w");
if (fp == NULL) {
env->ReleaseStringUTFChars(jTimeStr, time_str);
env->ReleaseStringUTFChars(path, path_str);
return env->NewStringUTF(file_path); // 处理文件打开失败
}
fputs(content, fp);
fclose(fp);
// 释放资源
env->ReleaseStringUTFChars(jTimeStr, time_str);
env->ReleaseStringUTFChars(path, path_str);
return env->NewStringUTF("success");
}
代码中我们调用Company对象中的getTime方法获取时间戳,完了追加内容后写入到指定path路径下。我们测试下,写入到手机download目录下:
// 执行 SD 卡读写操作
private void performSDCardOperation() {
String result = writeContentToFile(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath());
Log.e("carey" , "结果 " + result);
}
这里需要我们添加读写权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
performSDCardOperation();
} else {
// 请求权限
requestPermissions(PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_EXTERNAL_STORAGE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 权限被授予,执行读写操作
performSDCardOperation();
} else {
// 请授予权限
}
}
}
调用方法写入后,可以查看到后台日志:
同时我们在手机download目录下可以看到写入的文件内容:
四. 总结
今天学习了jni调用java类的无参和有参构造函数和成员方法,并将指定内容写入到指定目录下的文件中。喜欢的点赞收藏和订阅,感谢!