从C调用Android代码,需要用到JNI函数。需要将Android平台上的某些操作用C函数进行封装。下面就几个关键点给出说明。有不对的地方,也欢迎大家指正。
JNI_OnLoad
这个函数在加载.so时会被调用,我们要在这个函数中保存JavaVM指针。如果有全局初始化需要做,也可以放在这里面。函数的声明在jni.h中,这里给出一个简单的示例。
#define NZLOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, "NZ", fmt, ##args)
#define NZLOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, "NZ", fmt, ##args)
#define NZLOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, "NZ", fmt, ##args)
static JavaVM *g_javavm = nullptr;
jobject android_app_Application_globaljobject = nullptr;
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
NZLOGI("JNI_OnLoad");
NZLOGI("__ANDROID_API__ %d", __ANDROID_API__);
g_javavm = vm;
(void)reserved;
JNIEnv *env = NZJNI_GetEnv();
if (env) {
jobject application_jobject = NZJNI_GetApplication();
NZJNI_ClearException(env);
if (application_jobject) {
android_app_Application_globaljobject = env->NewGlobalRef(application_jobject);
env->DeleteLocalRef(application_jobject);
}
}
return JNI_VERSION_1_6;
}
JNI_OnUnload
这个函数在卸载.so时会被调用,我们只需要把在OnLoad中申请的资源释放掉就可以了。示例如下:
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved)
{
NZLOGI("JNI_OnUnload");
(void)vm;
(void)reserved;
JNIEnv *env = NZJNI_GetEnv();
if (env) {
if (android_app_Application_globaljobject) {
env->DeleteGlobalRef(android_app_Application_globaljobject);
android_app_Application_globaljobject = nullptr;
}
}
}
JNIEnv
这个是JNIEnv是可以用来新建Java对象实例并调用对象方法的。值得注意的地方有两个:
- 这个JNIEnv必须每次调用时都要重新获取。
- 在C环境下创建的子线程中,获取JNIEnv必须要AttachCurrentThread
例子如下:
JNIEnv *NZJNI_GetEnv()
{
JNIEnv *env = nullptr;
if (g_javavm) {
if (g_javavm->GetEnv((void **)(&env), JNI_VERSION_1_6) != JNI_OK) {
NZLOGE("NZJNI_GetEnv can't get the enviroument");
}
} else {
NZLOGE("NZJNI_GetEnv null javavm");
}
return env;
}
JNIEnv *NZJNI_AttachCurrentThread()
{
JNIEnv *env = nullptr;
if (g_javavm) {
if (g_javavm->AttachCurrentThread(&env, nullptr) != JNI_OK) {
NZLOGE("NZJNI_AttachCurrentThread can't get the enviroument");
}
} else {
NZLOGE("NZJNI_AttachCurrentThread null javavm");
}
return env;
}
JNIEnv *NZJNI_AutoAttachAndGetEnv(bool *newAttached)
{
JNIEnv *env = nullptr;
if (g_javavm) {
jint result = g_javavm->GetEnv((void **) (&env), JNI_VERSION_1_6);
if (result == JNI_OK) {
*newAttached = false;
return env;
} else if (result == JNI_EDETACHED) {
if (g_javavm->AttachCurrentThread(&env, nullptr) == JNI_OK) {
*newAttached = true;
return env;
} else {
NZLOGE("NZJNI_AutoAttachAndGetEnv can't AttachCurrentThread ");
*newAttached = false;
return env;
}
} else {
NZLOGE("NZJNI_AutoAttachAndGetEnv can't GetEnv");
*newAttached = false;
return env;
}
} else {
NZLOGE("NZJNI_AutoAttachAndGetEnv null javavm");
return env;
}
}
void NZJNI_DetachCurrentThread()
{
if (g_javavm) {
if (g_javavm->DetachCurrentThread() != JNI_OK) {
NZLOGE("NZJNI_DetachCurrentThread failure");
}
} else {
NZLOGE("NZJNI_DetachCurrentThread null javavm");
}
}
调用示例,获取Application对象
主要就是调用FindClass,GetMethodID,CallMethod等函数。
示例如下:
jobject NZJNI_GetApplication()
{
JNIEnv *env = nullptr;
jclass ActivityThreadClass = nullptr;
jmethodID currentActivityThreadMethod = nullptr;
jobject currentActivityThread_jobject = nullptr;
jmethodID getApplicationMethod = nullptr;
jobject application_jobject = nullptr;
env = NZJNI_GetEnv();
if (!env) {
NZLOGE("NZJNI_GetApplication NZJNI_GetEnv failure");
goto delete_localref_and_return;
}
ActivityThreadClass = env->FindClass("android/app/ActivityThread");
if (!ActivityThreadClass) {
NZLOGE("NZJNI_GetApplication null ActivityThreadClass");
goto delete_localref_and_return;
}
currentActivityThreadMethod = env->GetStaticMethodID(ActivityThreadClass, "currentActivityThread", "()Landroid/app/ActivityThread;");
if (!currentActivityThreadMethod) {
NZLOGE("NZJNI_GetApplication null currentActivityThreadMethod");
goto delete_localref_and_return;
}
currentActivityThread_jobject = env->CallStaticObjectMethod(ActivityThreadClass, currentActivityThreadMethod);
if (!currentActivityThread_jobject) {
NZLOGE("NZJNI_GetApplication null currentActivityThread_jobject");
goto delete_localref_and_return;
}
getApplicationMethod = env->GetMethodID(ActivityThreadClass, "getApplication", "()Landroid/app/Application;");
if (!getApplicationMethod) {
NZLOGE("NZJNI_GetApplication null getApplicationMethod");
goto delete_localref_and_return;
}
application_jobject = env->CallObjectMethod(currentActivityThread_jobject, getApplicationMethod);
if (!application_jobject) {
NZLOGE("NZJNI_GetApplication null application_jobject");
goto delete_localref_and_return;
}
goto delete_localref_and_return;
delete_localref_and_return:
if (env) {
NZJNI_ClearException(env);
if (ActivityThreadClass)
env->DeleteLocalRef(ActivityThreadClass);
if (currentActivityThread_jobject)
env->DeleteLocalRef(currentActivityThread_jobject);
}
return application_jobject;
}
bool NZJNI_ClearException(JNIEnv *env)
{
if (env) {
jthrowable obj = env->ExceptionOccurred();
if (obj) {
env->ExceptionDescribe();
env->ExceptionClear();
env->DeleteLocalRef(obj);
return true;
}
}
return false;
}
网友评论