JNI 的使用(callback)
一:定义
JNI是 Java Native Interface的缩写
使用JNI可以使Java代码和其他语言写的代码(如C/C++代码)进行交互。
二:JNI 使用之前的配置(AS3.0以上)
- 首先在src/main 文件夹下创建jni文件夹
- 创建 .cpp文件Android.mk和Application.mk文件(如果配置JNI需要引入第三方的so库,需要在存放第三方so库的文件夹下创建一个Android.mk文件)
配置结束如图,其中libnative-radio.so为第三方库。
libs中的mk文件:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := native-radio
LOCAL_SRC_FILES := libnative-radio.so
include $(PREBUILT_SHARED_LIBRARY)
jni中的mk文件
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := RadioJni
LOCAL_SRC_FILES := RadioJni.cpp
LOCAL_LDLIBS := -lm -llog
LOCAL_SHARED_LIBRARIES := native-radio
include $(BUILD_SHARED_LIBRARY)
include $(LOCAL_PATH)/libs/Android.mk
Application.mk
APP_MODULES := RadioJni
APP_ABI := armeabi-v7a
APP_PLATFORM := android-14
三:JNI的使用
1. 在JAVA程序中,首先需要在类中声明所调用的库名称,并且可以不用写出扩展名,系统会自己判定,如下:
static {
System.loadLibrary(“RadioJni”);
}
并且程序入口调用
mRadioJni = new RadioJni();
mRadioJni.open(_seekState, _muteState);
2.定义 native 方法
这里是将要调用的方法做本地声明,关键字是 :native
package com.neusoft.optimus.blaster.specific;
import android.util.Log;
import com.neusoft.optimus.blaster.specific.receiver.RadioEventReceiver;
public class RadioJni {
private static final String cTag = "RadioJni";
//定义的native方法 open(RadioSeekState state);
public native boolean open(RadioSeekState state);
public RadioEventReceiver radioEventReceiver;
public RadioEventReceiver getRadioEventReceiver() {
return radioEventReceiver;
}
public void setRadioEventReceiver(RadioEventReceiver radioEventReceiver) {
this.radioEventReceiver = radioEventReceiver;
}
//此方法虽然不是native方法,但是却是实现jni callback的关键方法,此方法的方法名可任意定义。
public void seekCallback(RadioSeekState state){
Log.i(cTag,"seekCallback RadioSeekState:" + state);
if (state != null){
if (radioEventReceiver != null){
radioEventReceiver.registerFreqChangeReceiver(state);
}
3.使用JAVAH命令生成 .h 文件(也可以自己手写.cpp文件,一步到位),并且实现。
手写 .cpp时,可以这样写:将声明native方法的类,包名中所有的 . 替换成_,并且在开头加上Java_,结尾加上_native的方法名
#include <jni.h>
#include "Radio.h"
#include <string.h>
#include <dlfcn.h>
#include <cstddef>
#include <stddef.h>
#ifndef _Included_com_neusoft_optimus_blaster_specific_RadioJni
#define _Included_com_neusoft_optimus_blaster_specific_RadioJni
#ifdef __cplusplus
extern "C" {
#endif
#include <android/log.h>
#define LOG_TAG "RadioJniTAG" // 这个是自定义的LOG的标识
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG, __VA_ARGS__)
using namespace xui::adaptapi::radio;
Radio* radio;
static jobject object;
static jobject seekStateObject;
SeekCallback seekCallback;
JavaVM *g_JavaVM;
//Dalvik虚拟机加载C库时,第一件事是调用JNI_OnLoad()函数,所以在JNI_OnLoad()里面进行一些初始化工作,如注册JNI函数等等。注册本地函数,可以加快java层调用本地函数的效率。
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
{
JNIEnv *env = NULL;
g_JavaVM=vm;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
goto fail;
}
if(env == NULL){
goto fail;
}
result = JNI_VERSION_1_4;
fail:
return result;
}
void seekResultCallback(RadioSeekState state) {
JNIEnv* jniEnv = NULL;
LOGI("seekResultCallback state frequency %d", state.frequency);
int status = g_JavaVM->GetEnv((void**)&jniEnv, JNI_VERSION_1_4);
if(status < 0)
{
g_JavaVM->AttachCurrentThread(&jniEnv,NULL);
}
jclass RadioJniClass = jniEnv->GetObjectClass(object); //返回对象的类。
if (RadioJniClass == NULL) {
LOGI("RadioJniClass == NULL");
return;
}
jmethodID seekCallback = jniEnv->GetMethodID(RadioJniClass, "seekCallback", "(Lcom/neusoft/optimus/blaster/specific/RadioSeekState;)V"); // 第一参数是Java 类对象。第二个参数是参数(或方法名),第三个参数是该参数(或方法)的签名
if (seekCallback == NULL) {
LOGI("seekCallback == NULL");
return;
}
jclass RadioStateClass = jniEnv->GetObjectClass(seekStateObject);//返回对象的类。
if (RadioStateClass == NULL) {
LOGI("RadioStateClass == NULL");
return;
}
//下面就是通过jni,将值赋值给java层的代码,实现callback
jmethodID construction_id = jniEnv->GetMethodID(RadioStateClass,"<init>", "()V");// 构造方法的方法名使用<init>代替
jobject radioState = jniEnv->NewObject(RadioStateClass, construction_id);// NewObject需要指明调用的构造方法,构建一个新的类对象,并初始化成员变量,调用指定的构造方法
jfieldID frequency = jniEnv->GetFieldID(RadioStateClass,"frequency","I");// 第一个参数:Java类对象;第二个参数:参数名(或方法名);第三个参数:该参数(或方法)的签名;
jfieldID seekFrequencyStatus = jniEnv->GetFieldID(RadioStateClass,"seekFrequencyStatus","I");
jfieldID audioChannelCfg = jniEnv->GetFieldID(RadioStateClass,"audioChannelCfg","I");
//到下面才是真正的赋值,第一个参数:java中对应类对象 第二个参数:这个是java层级的参数 第三个参数:这个是变化的参数,通过该方法将变化的参数实时设置给java层,实现callback
jniEnv->SetIntField(radioState, frequency, state.frequency);
jniEnv->SetIntField(radioState, seekFrequencyStatus, state.isStation);
jniEnv->SetIntField(radioState, audioChannelCfg, state.audioChannelCfg);
//这里是callback“调用”java的方法seekCallback,到此结束了jni 的 callback
jniEnv->CallVoidMethod(object, seekCallback, radioState);
jniEnv->DeleteLocalRef(RadioJniClass);
jniEnv->DeleteLocalRef(radioState);
jniEnv->DeleteLocalRef(RadioStateClass);
}
/*
* Class: com_neusoft_optimus_blaster_specific_RadioJni
* Method: open
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_com_neusoft_optimus_blaster_specific_RadioJni_open(JNIEnv * env, jobject ob ,jobject seekStateobj){
object = env->NewGlobalRef(ob);
seekStateObject = env->NewGlobalRef(seekStateobj); //这里将java层调用 open方法传递的参数进行保存,用于seekResultCallback方法寻找java中类
seekCallback = seekResultCallback; //此处是实现callback 的关键,
radio = Radio::get();
bool openResult = radio->open(seekCallback); //这里会执行到c++的代码中
return (jboolean) openResult;
}
//当虚拟机释放该C库时,则会调用JNI_OnUnload()函数来进行善后清除动作。
//在此方法中手动释放不用的对象
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved)
{
LOGI("JNI_OnUnload");
JNIEnv* jniEnv = NULL;
vm->GetEnv((void**)&jniEnv, JNI_VERSION_1_4);
jniEnv->DeleteGlobalRef(object);
jniEnv->DeleteGlobalRef(seekStateObject);
delete radio;
delete object;
delete seekStateObject;
}
#ifdef __cplusplus
}
#endif
#endif
4.用到的类
public class RadioSeekState {
// 搜台时,此频率状态
public int frequency; // 频率
public int seekFrequencyStatus; // 频率状态
public int audioChannelCfg; // 声音通道
public int band; // band
public class RadioState {
public int frequency; // 频率
}
四:总结
jni的实现最中的就是
1.首先需要在类中声明所调用的库名称
2.定义native方法
第一次写,上面是自己的一些理解,如果有不对的地方,希望大神支出!!!