Android Audio代码分析25 - JNI callback

今天来说说 native 中的代码是如何调用 java 侧代码的。
在看 setEnabled 代码的时候,我们了解到,最终在函数 EffectHandle::setEnabled 中会调用 java 侧的函数, 
将状态改变的事件通知到 java 侧。
今天就以 AudioEffect 中的 native 侧调用 java 函数作为例子,来说明 JNI 中的 callback 函数。


#######################说明################################
/*1、首先,在 JNI 中会有一个 init 函数,java 侧会首先调用该 init 函数来完成初始化。
    对于 AudioEffect 来说,该函数就是 android_media_AudioEffect_native_init 函数。
*/
// ++++++++++++++++++++++++++++++android_media_AudioEffect_native_init++++++++++++++++++++++++++++++++++
// This function gets some field IDs, which in turn causes class initialization.
// It is called from a static block in AudioEffect, which won't run until the
// first time an instance of this class is used.
static void
android_media_AudioEffect_native_init(JNIEnv *env)
{


    LOGV("android_media_AudioEffect_native_init");


    fields.clazzEffect = NULL;
    fields.clazzDesc = NULL;


    // Get the AudioEffect class
// 获取 java 侧的类
// static const char* const kClassPathName = "android/media/audiofx/AudioEffect";
// 此处的 path name 其实是 java 侧类所在的包。
// java 侧, AudioEffect 类所在地包为 package android.media.audiofx;
    jclass clazz = env->FindClass(kClassPathName);
    if (clazz == NULL) {
        LOGE("Can't find %s", kClassPathName);
        return;
    }


// 创建 java 侧 AudioEffect 对象的一个全局引用
    fields.clazzEffect = (jclass)env->NewGlobalRef(clazz);


    // Get the postEvent method
// 根据函数名称及参数类型从 AudioEffect 对象中获取函数
    fields.midPostNativeEvent = env->GetStaticMethodID(
            fields.clazzEffect,
            "postEventFromNative",                           // 函数名
"(Ljava/lang/Object;IIILjava/lang/Object;)V");   // 参数类型(object, int, int, int, object)
    if (fields.midPostNativeEvent == NULL) {
        LOGE("Can't find AudioEffect.%s", "postEventFromNative");
        return;
    }


    // Get the variables fields
    //      nativeTrackInJavaObj
    fields.fidNativeAudioEffect = env->GetFieldID(
            fields.clazzEffect,
            "mNativeAudioEffect", "I");
    if (fields.fidNativeAudioEffect == NULL) {
        LOGE("Can't find AudioEffect.%s", "mNativeAudioEffect");
        return;
    }
    //      fidJniData;
    fields.fidJniData = env->GetFieldID(
            fields.clazzEffect,
            "mJniData", "I");
    if (fields.fidJniData == NULL) {
        LOGE("Can't find AudioEffect.%s", "mJniData");
        return;
    }


// 查找类 Descriptor
    clazz = env->FindClass("android/media/audiofx/AudioEffect$Descriptor");
    if (clazz == NULL) {
        LOGE("Can't find android/media/audiofx/AudioEffect$Descriptor class");
        return;
    }
    fields.clazzDesc = (jclass)env->NewGlobalRef(clazz);


// 得到 Descriptor 的 init 函数
    fields.midDescCstor
            = env->GetMethodID(
                    fields.clazzDesc,
                    "<init>",
                    "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
    if (fields.midDescCstor == NULL) {
        LOGE("Can't find android/media/audiofx/AudioEffect$Descriptor class constructor");
        return;
    }
}
// ------------------------------android_media_AudioEffect_native_init----------------------------------

/*
2、 java 侧会调用 native_setup 函数, 该函数中会创建 native 侧对象。
    对应 AudioEffect 来说, native_setup 函数就是 android_media_AudioEffect_native_setup 函数。*/
// ++++++++++++++++++++++++++++android_media_AudioEffect_native_setup++++++++++++++++++++++++++++++++++++
static jint
android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
        jstring type, jstring uuid, jint priority, jint sessionId, jintArray jId, jobjectArray javadesc)
{
    LOGV("android_media_AudioEffect_native_setup");
    AudioEffectJniStorage* lpJniStorage = NULL;
    int lStatus = AUDIOEFFECT_ERROR_NO_MEMORY;
    AudioEffect* lpAudioEffect = NULL;
    jint* nId = NULL;
    const char *typeStr = NULL;
    const char *uuidStr = NULL;
    effect_descriptor_t desc;
    jobject jdesc;
    char str[EFFECT_STRING_LEN_MAX];
    jstring jdescType;
    jstring jdescUuid;
    jstring jdescConnect;
    jstring jdescName;
    jstring jdescImplementor;


    if (type != NULL) {
        typeStr = env->GetStringUTFChars(type, NULL);
        if (typeStr == NULL) {  // Out of memory
            jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
            goto setup_failure;
        }
    }


    if (uuid != NULL) {
        uuidStr = env->GetStringUTFChars(uuid, NULL);
        if (uuidStr == NULL) {  // Out of memory
            jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
            goto setup_failure;
        }
    }


    if (typeStr == NULL && uuidStr == NULL) {
        lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
        goto setup_failure;
    }


    lpJniStorage = new AudioEffectJniStorage();
    if (lpJniStorage == NULL) {
        LOGE("setup: Error creating JNI Storage");
        goto setup_failure;
    }


// 创建 java 侧 AudioEffect 对象的一个全局引用, 并将其保存到 AudioEffectJniStorage 对象中
    lpJniStorage->mCallbackData.audioEffect_class = (jclass)env->NewGlobalRef(fields.clazzEffect);
    // we use a weak reference so the AudioEffect object can be garbage collected.
// weak_this 是 java 侧 AudioEffect 对象的一个 weak reference , 通过 native_setup 函数传进来
    lpJniStorage->mCallbackData.audioEffect_ref = env->NewGlobalRef(weak_this);


    LOGV("setup: lpJniStorage: %p audioEffect_ref %p audioEffect_class %p, &mCallbackData %p",
            lpJniStorage,
            lpJniStorage->mCallbackData.audioEffect_ref,
            lpJniStorage->mCallbackData.audioEffect_class,
            &lpJniStorage->mCallbackData);


    if (jId == NULL) {
        LOGE("setup: NULL java array for id pointer");
        lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
        goto setup_failure;
    }


    // create the native AudioEffect object
    lpAudioEffect = new AudioEffect(typeStr,
                                    uuidStr,
                                    priority,
                                    effectCallback, // 真正实现在 native 侧调用 java 侧代码的函数
                                    &lpJniStorage->mCallbackData, // 创建 native 侧 AudioEffect 对象,将 lpJniStorage->mCallbackData 作为 user data 传入
                                    sessionId,
                                    0);
    if (lpAudioEffect == NULL) {
        LOGE("Error creating AudioEffect");
        goto setup_failure;
    }


    lStatus = translateError(lpAudioEffect->initCheck());
    if (lStatus != AUDIOEFFECT_SUCCESS && lStatus != AUDIOEFFECT_ERROR_ALREADY_EXISTS) {
        LOGE("AudioEffect initCheck failed %d", lStatus);
        goto setup_failure;
    }


    nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL);
    if (nId == NULL) {
        LOGE("setup: Error retrieving id pointer");
        lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
        goto setup_failure;
    }
    nId[0] = lpAudioEffect->id();
    env->ReleasePrimitiveArrayCritical(jId, nId, 0);
    nId = NULL;


    if (typeStr) {
        env->ReleaseStringUTFChars(type, typeStr);
        typeStr = NULL;
    }


    if (uuidStr) {
        env->ReleaseStringUTFChars(uuid, uuidStr);
        uuidStr = NULL;
    }


    // get the effect descriptor
    desc = lpAudioEffect->descriptor();


    AudioEffect::guidToString(&desc.type, str, EFFECT_STRING_LEN_MAX);
    jdescType = env->NewStringUTF(str);


    AudioEffect::guidToString(&desc.uuid, str, EFFECT_STRING_LEN_MAX);
    jdescUuid = env->NewStringUTF(str);


    if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
        jdescConnect = env->NewStringUTF("Auxiliary");
    } else {
        jdescConnect = env->NewStringUTF("Insert");
    }


    jdescName = env->NewStringUTF(desc.name);
    jdescImplementor = env->NewStringUTF(desc.implementor);


// 创建一个 Descriptor 对象。
// 使用的函数是在函数 android_media_AudioEffect_native_init 中得到的 Descriptor 的 init 函数
    jdesc = env->NewObject(fields.clazzDesc,
                           fields.midDescCstor,
                           jdescType,
                           jdescUuid,
                           jdescConnect,
                           jdescName,
                           jdescImplementor);
    env->DeleteLocalRef(jdescType);
    env->DeleteLocalRef(jdescUuid);
    env->DeleteLocalRef(jdescConnect);
    env->DeleteLocalRef(jdescName);
    env->DeleteLocalRef(jdescImplementor);
    if (jdesc == NULL) {
        LOGE("env->NewObject(fields.clazzDesc, fields.midDescCstor)");
        goto setup_failure;
    }


    env->SetObjectArrayElement(javadesc, 0, jdesc);


    env->SetIntField(thiz, fields.fidNativeAudioEffect, (int)lpAudioEffect);


    env->SetIntField(thiz, fields.fidJniData, (int)lpJniStorage);


    return AUDIOEFFECT_SUCCESS;


    // failures:
setup_failure:


    if (nId != NULL) {
        env->ReleasePrimitiveArrayCritical(jId, nId, 0);
    }


    if (lpAudioEffect) {
        delete lpAudioEffect;
    }
    env->SetIntField(thiz, fields.fidNativeAudioEffect, 0);


    if (lpJniStorage) {
        delete lpJniStorage;
    }
    env->SetIntField(thiz, fields.fidJniData, 0);


    if (uuidStr != NULL) {
        env->ReleaseStringUTFChars(uuid, uuidStr);
    }


    if (typeStr != NULL) {
        env->ReleaseStringUTFChars(type, typeStr);
    }


    return lStatus;
}
// ----------------------------android_media_AudioEffect_native_setup------------------------------------

/*
3、在 native 侧对象的构造函数中,会将传入的包含 java 侧对象和函数的 callback data 赋值给自己的成员变量。
    同时将传入的 callback 函数赋值给自己的成员变量。

4、 native 侧对象的构造函数中,会创建一个 client 对象(mIEffectClient), 并将 native 侧 AudioTrack 对象的指针作为参数传入。
     client 对象的构造函数中会将传入的 AudioTrack 对象的指针赋值给自己的成员变量(mEffect)。
 
5、 native 侧 AudioTrack 对象的构造函数中,接下来调用函数 AudioFlinger::createEffect , 并将 mIEffectClient 作为参数传入。


6、函数 AudioFlinger::createEffect 中调用函数 AudioFlinger::PlaybackThread::createEffect_l ,并继续将 effectClient 下传。


7、函数 AudioFlinger::PlaybackThread::createEffect_l 中会创建一个 EffectHandle 对象,并将 effectClient 传入。


8、 AudioFlinger::EffectHandle::EffectHandle 的构造函数中将传入的 client 对象赋值给了自己的成员变量 mEffectClient 。


9、 函数 AudioFlinger::EffectHandle::setEnabled 会调用 mEffectClient 的 enableStatusChanged 函数。*/
// ++++++++++++++++++++++++++AudioFlinger::EffectHandle::setEnabled++++++++++++++++++++++++++++++++++++++
void AudioFlinger::EffectHandle::setEnabled(bool enabled)
{
    if (mEffectClient != 0) {
        mEffectClient->enableStatusChanged(enabled);
    }
}
// --------------------------AudioFlinger::EffectHandle::setEnabled--------------------------------------

/*
10、 函数 EffectClient::enableStatusChanged 直接调用了函数 AudioEffect::enableStatusChanged 。


11、函数 AudioEffect::enableStatusChanged 的实现。 其中的 mCbf 其实就是函数 effectCallback 。*/
// +++++++++++++++++++++++++++AudioEffect::enableStatusChanged+++++++++++++++++++++++++++++++++++++
void AudioEffect::enableStatusChanged(bool enabled)
{
    LOGV("enableStatusChanged %p enabled %d mCbf %p", this, enabled, mCbf);
    if (mStatus == ALREADY_EXISTS) {
        if (enabled) {
            android_atomic_or(1, &mEnabled);
        } else {
            android_atomic_and(~1, &mEnabled);
        }
        if (mCbf) {
            mCbf(EVENT_ENABLE_STATUS_CHANGED, mUserData, &enabled);
        }
    }
}
// ---------------------------AudioEffect::enableStatusChanged-------------------------------------


// 12、函数 effectCallback 的实现:
// +++++++++++++++++++++++++++++effectCallback+++++++++++++++++++++++++++++++++++
static void effectCallback(int event, void* user, void *info) {


    effect_param_t *p;
    int arg1 = 0;
    int arg2 = 0;
    jobject obj = NULL;
    jbyteArray array = NULL;
    jbyte *bytes;
    bool param;
    size_t size;


    effect_callback_cookie *callbackInfo = (effect_callback_cookie *)user;
    JNIEnv *env = AndroidRuntime::getJNIEnv();


    LOGV("effectCallback: callbackInfo %p, audioEffect_ref %p audioEffect_class %p",
            callbackInfo,
            callbackInfo->audioEffect_ref,
            callbackInfo->audioEffect_class);


    if (!user || !env) {
        LOGW("effectCallback error user %p, env %p", user, env);
        return;
    }


    switch (event) {
    case AudioEffect::EVENT_CONTROL_STATUS_CHANGED:
        if (info == 0) {
            LOGW("EVENT_CONTROL_STATUS_CHANGED info == NULL");
            goto effectCallback_Exit;
        }
        param = *(bool *)info;
        arg1 = (int)param;
        LOGV("EVENT_CONTROL_STATUS_CHANGED");
        break;
    case AudioEffect::EVENT_ENABLE_STATUS_CHANGED:
        if (info == 0) {
            LOGW("EVENT_ENABLE_STATUS_CHANGED info == NULL");
            goto effectCallback_Exit;
        }
        param = *(bool *)info;
        arg1 = (int)param;
        LOGV("EVENT_ENABLE_STATUS_CHANGED");
        break;
    case AudioEffect::EVENT_PARAMETER_CHANGED:
        if (info == 0) {
            LOGW("EVENT_PARAMETER_CHANGED info == NULL");
            goto effectCallback_Exit;
        }
        p = (effect_param_t *)info;
        if (p->psize == 0 || p->vsize == 0) {
            goto effectCallback_Exit;
        }
        // arg1 contains offset of parameter value from start of byte array
        arg1 = sizeof(effect_param_t) + ((p->psize - 1) / sizeof(int) + 1) * sizeof(int);
        size = arg1 + p->vsize;
        array = env->NewByteArray(size);
        if (array == NULL) {
            LOGE("effectCallback: Couldn't allocate byte array for parameter data");
            goto effectCallback_Exit;
        }
        bytes = env->GetByteArrayElements(array, NULL);
        memcpy(bytes, p, size);
        env->ReleaseByteArrayElements(array, bytes, 0);
        obj = array;
        LOGV("EVENT_PARAMETER_CHANGED");
       break;
    case AudioEffect::EVENT_ERROR:
        LOGW("EVENT_ERROR");
        break;
    }


// 此处实现了对 java 侧函数的调用
    env->CallStaticVoidMethod(
        callbackInfo->audioEffect_class,  // java 侧的对象的引用
        fields.midPostNativeEvent,  // java 侧的函数, 在函数 android_media_AudioEffect_native_init 中 
        callbackInfo->audioEffect_ref, event, arg1, arg2, obj); // 这几个都是调用 java 侧函数时的参数


effectCallback_Exit:
    if (array) {
        env->DeleteLocalRef(array);
    }


    if (env->ExceptionCheck()) {
        env->ExceptionDescribe();
        env->ExceptionClear();
    }
}
// -----------------------------effectCallback-----------------------------------


// 13、 java 侧 postEventFromNative 函数的实现:
// +++++++++++++++++++++++++++postEventFromNative+++++++++++++++++++++++++++++++++++++
    private static void postEventFromNative(Object effect_ref, int what,
            int arg1, int arg2, Object obj) {
        AudioEffect effect = (AudioEffect) ((WeakReference) effect_ref).get();
        if (effect == null) {
            return;
        }
        if (effect.mNativeEventHandler != null) {
            Message m = effect.mNativeEventHandler.obtainMessage(what, arg1,
                    arg2, obj);
            effect.mNativeEventHandler.sendMessage(m);
        }


    }
// ---------------------------postEventFromNative-------------------------------------

###########################################################


&&&&&&&&&&&&&&&&&&&&&&&总结&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
上面列出了 native 代码中调用 java 函数的过程。
其实,从 java 侧对象的创建时机来看, JNI 回调分为两种情况:
java 对象在 java 侧创建
java 对象中 JNI 中创建。


一、java 对象在 java 侧创建
    我们上面介绍的 AudioEffect 中的回调,就是这种方式。
这种情况下,主控程序在 java 侧。
1、调用函数 env->FindClass 找到指定的类。
2、调用 env->NewGlobalRef 函数获取对象的引用。
3、调用 env->GetStaticMethodID 函数获取 java 侧的函数。
4、在 JNI 中定义一个 callback 函数调用 java 侧的函数。
5、创建 native 侧对象的时候将 callback 函数作为参数传给 native 对象的构造函数。
6、 native 对象就可以通过 callback 函数来调用 java 侧的函数。

二、java 对象中 JNI 中创建
    这种情况下,主控程序在 native 侧。
1、调用函数 env->FindClass 找到指定的类。
2、调用 env->NewGlobalRef 函数获取对象的引用。
3、调用函数 env->GetMethodID 得到 java 侧创建对象的函数。
4、调用函数 env->NewObject ,通过第三步中得到的函数创建 java 侧对象。
5、调用 env->GetStaticMethodID 函数获取 java 侧的函数。
6、在 JNI 中定义一个 callback 函数调用 java 侧的函数。
7、创建 native 侧对象的时候将 callback 函数作为参数传给 native 对象的构造函数。
8、 native 对象就可以通过 callback 函数来调用 java 侧的函数。
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值