15235313_z0yP

Android JNI开发基础(二)

如何使用JNI

调用Java函数

jclass cls = (*env)->GetObjectClass(env, thiz);
jmethodID methodId = (*env)->GetMethodID(env, cls, "getAout", "()I");
bool use_opensles = (*env)->CallIntMethod(env, thiz, methodId) == AOUT_OPENSLES;
// ...
(*env)->DeleteLocalRef(env, cls);
  • GetObjectClass获取得到调用JNI函数当前对象的类,如果不是当前对象的类,使用FindClass(JNIEnv *env, const char* className)
  • GetMethodID获取调用JNI函数当前对象的类函数,getAout为Java函数名,()I为输入参数和返回值签名,签名的规则见后文
  • CallIntMethod调用Java对象的getAout函数,返回值是int类型,无返回值函数调用CallVoidMethod,其他类型返回值依此类推,静态函数调用CallStaticTypeMethod
  • DeleteLocalRef删除引用,cls使用完后,记得删除引用计数

JNI回调函数的调用Java函数的方法如下:

JavaVM *myVm;
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    myVm = vm; // Keep a reference on the Java VM.
    return JNI_VERSION_1_2;
}
static void CallbackFunc() {
    JNIEnv *env;
    if ((*myVm)->AttachCurrentThread(myVm, &env, NULL) < 0)         return;     // ...     (*myVm)->DetachCurrentThread(myVm);
} 
  • 在JNI_OnLoad里保存JavaVM
  • 调用AttachCurrentThread获得JNIEnv,使用JNIEnv调用Java函数
  • 调用DetachCurrentThread解绑

Java函数变量签名

Java Type Native Type Type Signature
boolean jboolean Z
byte jbyte B
char jchar C
short jshort S
int jint I
long jlong J
float jfloat F
double jdouble D
void void V
String jstring Ljava/lang/String;

加入Log

#include <android/log.h>
#define LOG_TAG "System.out”

#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

LOGI("info\n");
LOGD("debug\n");
LOGD(“The string is %s”, p_str);
  • Android.mk文件增加LOCAL_LDLIBS += -llog
  • 在LogCat里可以看到JNI输出的日志信息

Android.mk

LOCAL_PATH := $(call my-dir) // ①

include $(CLEAR_VARS) // ②

LOCAL_MODULE    := hello-jni // ③
LOCAL_SRC_FILES := hello-jni.c // ④

include $(BUILD_SHARED_LIBRARY) // ⑤

① LOCAL_PATH用于指定源代码的目录,my-dir宏函数是编译环境提供,表示当前文件夹
② CLEAR_VARS由编译系统提供,用于清除一些LOCAL_XXX的值(如:LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES …),不清除LOCAL_PATH
③ LOCAL_MODULE定义模块名称标识,名称必须唯一,不能包含空格,编译系统会编译生成lib.so或lib.a文件
④ LOCAL_SRC_FILES指定要编译的.c和.cpp文件,多个文件以空格分隔,换行连接符为 \
⑤ 编译成共享库,生成so文件,还可以是BUILD_STATIC_LIBRARY,编译静态库,即a文件,.a文件必须链接到so文件里
LOCAL_LDLIBS := -llog –lz 加载liblog.a静态库和libz.a静态库,这两个静态库由NDK提供,也可以指定搜索路径加载编译好的静态库,使用-Lpath_to_lib/xxx.a
LOCAL_STATIC_LIBRARIES += hello-jni表示编译依赖的静态库,在项目里的其他Android.mk文件定义
LOCAL_C_INCLUDES += include指定include路径
LOCAL_CFLAGS += -DANDROID=1定义ANDROID宏的值为1
多个共享库都需要调用loadLibrary加载,包括依赖的so库,Android不会自动寻找依赖库

内存管理

  1. 全局引用
    NewGlobalRef

    jobject NewGlobalRef(JNIEnv *env, jobject obj);

    创建一个新的obj全局引用,调用DeleteGlobalRef()释放全局引用

  2. 局部引用
    • 每一个传入到native函数的参数和返回值都是本地引用,其值只有在当前线程的native函数调用期间有效,当native函数返回,引用失效。适用于所以jobject派生类,包括jclass, jstring, jarray等。
    • 判断两个object引用是否一样,不能使用==,要使用函数IsSameObject。
    • 不能过度分配局部引用,当创建了大量的局部引用的适合,必须调用DeleteLocalRef手动释放局部引用,Android支持最多512个局部引用。
      jfieldID和jmethodID不是引用对象。

课后小练习

  • 项目名:wav to mp3转码器
  • 需求:
    将wav文件转换成mp3文件
    实现基本参数设置
    实现转换进度显示
    WAV和mp3文件播放(可选)
  • 实现方案:
    采用LAME库,封装成JNI,实现转换功能
    采用Android MediaPlayer实现播放功能
  • 参考代码:https://github.com/jackwuwei/wav2mp3-android

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

This site uses Akismet to reduce spam. Learn how your comment data is processed.