JNI基础
AS新建C++工程
【JNI基础】在新建项目的面板,勾选Include C++ support,新建的项目自动包含C++配置。
C++生成Java中定义的native方法
新生成的项目已包含C++生成Java的native方法范本,新建的native方法,只需使用AS快捷键Alt+Enter自动生成。如:
public native String stringFromJNI();
在c++中注册的方法为:
extern "C"
JNIEXPORT jstring JNICALL Java_com_example_myjnitest_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
C++生成的方法为:extern "C" JNIEXPORT {返回类型} JNICALL {方法名} {方法体}
方法名对应格式为:Java_全路径名类名_方法名
参数必须包含两个默认参数JNIEnv *env,jobject instance
若Java方法包含参数,则c++方法在上面两个参数后面新增对应类型的参数
C++中获取方法中的参数
获取Java参数
- 获取int和String参数
native String test0(int i,String s);
extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_myjnitest_MainActivity_test0(JNIEnv *env, jobject instance, jint i, jstring s_) {
LOGE("获取java中int数据:%d",i);
//获取java的String
const char* s = env->GetStringUTFChars(s_,0);
LOGE("获取java中String数据:%s",s);
env->ReleaseStringUTFChars(s_, s);
return env->NewStringUTF(s);
}
- 获取int数组和String数组参数
native void test1(int[] i,String[] j);
extern "C"
JNIEXPORT void JNICALL
Java_com_example_myjnitest_MainActivity_test1(JNIEnv *env, jobject instance, jintArray i_,
jobjectArray j) {
//遍历int数组
jint *i = env->GetIntArrayElements(i_,NULL);
int32_t length = env->GetArrayLength(i_);
for (int k = 0;
k < length;
++k) {
LOGE("遍历java的int数组:%d",*(i+ k));
}
env->ReleaseIntArrayElements(i_,i,0);
//遍历String数组
int32_t strLength = env->GetArrayLength(j);
for (int k = 0;
k < strLength;
++k) {
jstring str = static_cast(env->GetObjectArrayElement(j, k));
const char* s = env->GetStringUTFChars(str,0);
LOGE("获取java的String:%s",s);
env->ReleaseStringUTFChars(str,s);
}
}
- 获取Java中的自定义Bean参数
native void test2(Bean bean);
extern "C"
JNIEXPORT void JNICALL
Java_com_example_myjnitest_MainActivity_test2(JNIEnv *env, jobject instance, jobject bean) {
//1.反射获取java对应的对象
jclass beanCls = env->GetObjectClass(bean);
//2.反射获取要调用的方法,第三个参数为签名
jmethodID getName = env->GetMethodID(beanCls,"getName","()Ljava/lang/String;
");
//3.调用
jstring name = static_cast(env->CallObjectMethod(bean, getName));
const char* nameStr = env->GetStringUTFChars(name,NULL);
LOGE("获取到Bean的name:%s" ,nameStr);
env->ReleaseStringUTFChars(name,nameStr);
//获取setAge方法修改age参数,调用getAge方法获取age的值
jmethodID setAge = env->GetMethodID(beanCls,"setAge","(I)V");
env->CallVoidMethod(bean,setAge,88);
jmethodID getAge = env->GetMethodID(beanCls,"getAge","()I");
int32_t age = env->CallIntMethod(bean,getAge);
LOGE("修改Bean的age:%d",age);
}
参数签名对照表:
Java类型 | 签名 |
---|---|
boolean | Z |
short | S |
float | F |
byte | B |
int | I |
double | D |
char | C |
long | J |
void | V |
引用类型 | L + 全限定名 + ; |
数组 | [+类型签名 |
extern "C"
JNIEXPORT void JNICALL
Java_com_example_myjnitest_MainActivity_test3(JNIEnv *env, jobject instance) {
//构造方法创建对象
jclass beanCls = env->FindClass("com/example/myjnitest/Bean");
jmethodID constuct = env->GetMethodID(beanCls,"","()V");
jobject bean = env->NewObject(beanCls,constuct);
//修改属性值
jfieldID nameId = env->GetFieldID(beanCls,"name","Ljava/lang/String;
");
jstring nameStr = env->NewStringUTF("张学友");
env->SetObjectField(bean,nameId,nameStr);
jfieldID ageId = env->GetFieldID(beanCls,"age","I");
env->SetIntField(bean,ageId,666);
//调用Bean中的方法
jmethodID print = env->GetMethodID(beanCls,"print","()V");
env->CallVoidMethod(bean,print);
}
JNI_OnLoad
调用System.loadLibrary()方法时,内部会查找so中的JNI_OnLoad方法,存在则调用。
动态注册
之前Java方法与C++方法匹配的方式,叫做静态注册。
另外,还可以使用动态注册,自定义方法名。
native void test4(int i);
native String test5(String s);
//对应Java的test4方法
void test4444(JNIEnv *env, jobject instance, jint i) {
LOGE("调用test4:%d", i);
}//对应Java的test5方法
jstring test55555(JNIEnv *env, jobject instance, jstring s) {
return env->NewStringUTF("返回test5");
}//需要动态注册的方法数组
static const JNINativeMethod mMethods[] = {
{"test4", "(I)V",(void *) test4444},
{"test5", "(Ljava/lang/String;
)Ljava/lang/String;
", (jstring *) test55555}
};
//需要动态注册native方法的类名
static const char *mClassName = "com/example/myjnitest/MainActivity";
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
//获取JNIEnv
int r = vm->GetEnv((void **) &env, JNI_VERSION_1_4);
if (r != JNI_OK) {
return -1;
}
jclass mainActivityCls = env->FindClass(mClassName);
r = env->RegisterNatives(mainActivityCls, mMethods, sizeof(mMethods) / sizeof(JNINativeMethod));
if (r != JNI_OK) {
return -1;
}
return JNI_VERSION_1_4;
}
native线程
native调用java需要使用JNIEnv这个结构体,而JNIEnv是由Jvm传入与线程相关的变量。可以通过JavaVM的AttachCurrentThread方法来获取到当前线程中的JNIEnv指针。
c++多线程调用MainActivity更新UI方法:
native void testThread();
public void updateUI() {
if (Looper.myLooper() == Looper.getMainLooper()) {
Toast.makeText(this, "C++更新UI", Toast.LENGTH_SHORT).show();
}else {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "C++更新UI", Toast.LENGTH_SHORT).show();
}
});
}
}
struct Context {
jobject activity;
};
JavaVM *_vm;
//JNI_OnLoad中赋值void* threadTask(void *args) {
//native线程 附加到 Java虚拟机
JNIEnv *env;
jint i = _vm->AttachCurrentThread(&env,0);
if(i != JNI_OK){
return 0;
}
//获取传入的MainActivity对象,调用MainActiviy中的updateUI方法
Context *context = static_cast(args);
jclass cls = env->GetObjectClass(context->activity);
jmethodID updateUI = env->GetMethodID(cls,"updateUI","()V");
env->CallVoidMethod(context->activity,updateUI);
env->DeleteGlobalRef(context->activity);
//释放资源
delete(context);
context = 0;
//分离
_vm->DetachCurrentThread();
return 0;
}//动态注册
void testThread(JNIEnv *env,jobject instance) {
pthread_t pid;
Context *context = new Context;
context->activity = env->NewGlobalRef(instance);
pthread_create(&pid,0,threadTask,context);
}
推荐阅读
- 六步搭建ES6语法环境
- 使用composer自动加载类文件
- Python基础|Python基础 - 练习1
- Java|Java基础——数组
- Java基础-高级特性-枚举实现状态机
- 营养基础学20180331(课间随笔)??
- iOS面试题--基础
- HTML基础--基本概念--跟着李南江学编程
- py连接mysql
- typeScript入门基础介绍