從C調(diào)用Android代碼矗钟,需要用到JNI函數(shù)遮糖。需要將Android平臺上的某些操作用C函數(shù)進(jìn)行封裝。下面就幾個關(guān)鍵點給出說明孽亲。有不對的地方慧妄,也歡迎大家指正顷牌。
JNI_OnLoad
這個函數(shù)在加載.so時會被調(diào)用,我們要在這個函數(shù)中保存JavaVM指針塞淹。如果有全局初始化需要做窟蓝,也可以放在這里面。函數(shù)的聲明在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
這個函數(shù)在卸載.so時會被調(diào)用,我們只需要把在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對象實例并調(diào)用對象方法的谁帕。值得注意的地方有兩個:
- 這個JNIEnv必須每次調(diào)用時都要重新獲取。
- 在C環(huán)境下創(chuàng)建的子線程中冯袍,獲取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");
}
}
調(diào)用示例匈挖,獲取Application對象
主要就是調(diào)用FindClass,GetMethodID康愤,CallMethod等函數(shù)儡循。
示例如下:
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;
}