从零开始编写JNI

Posted warmor

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从零开始编写JNI相关的知识,希望对你有一定的参考价值。

最近项目中用到了JNI,本以为很简单的,没想到花了我一天的时间才搞定。主要是在过程中遇到了一个大坑,下面就详细说说。

出现的问题是这样的:代码一运行到System.loadLibrary("xxx")时,就提示java.lang.UnsatisfiedLinkError(Failed to register native method xxx),很明显是在register时出错了,我经过多次尝试,才终于解决这个难题。借着这个问题,再来复习一下jni的整体编写流程吧!

话不多说,直接上代码,c文件如下:

#include <string.h>
#include <jni.h>
#include <android/log.h>
#include <sys/types.h>

#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
#define  LOG_TAG1    "ctldevs-jni"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG1,__VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG1,__VA_ARGS__)

jstring native_stringFromJNI( JNIEnv* env  , jobject thiz )
{
    return (*env)->NewStringUTF(env, "Hello from JNI--Wangdong 111!");//打印字符串 
}

static const char *classPathName = "com/wdong/myapp/MainActivity";

//注意 第一列参数(stringFromJNI),不能带下划线
static JNINativeMethod methods[] = {
       {"stringFromJNI", "()Ljava/lang/String;", (void*)native_stringFromJNI},
};

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jclass clazz;

    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        LOGE("ERROR: GetEnv failed\\n");
        return JNI_ERR;
    }
    clazz = (*env)->FindClass(env,classPathName);
    if (clazz == NULL)
    {
        LOGE("Native registration unable to find class '%s'", classPathName);
        return JNI_ERR;
    }

    if((*env)->RegisterNatives(env, clazz, methods, NELEM(methods)) < 0)
    {
        LOGE("ERROR: MediaPlayer native registration failed\\n");
           return JNI_ERR;
    }
 
   return JNI_VERSION_1_4;
}

之所以会报最开始的那个错,就是因为我给methods[]的第一个参数加了_,也就是写成了类似string_FromJNI这样,所以就报错了。因为这个小小的细节,耽误了我近一天的时间,真是有够郁闷的!

methods[]的第二个参数是 (参数)返回值 的形式,举几个例子:

jboolean ( JNIEnv* env  , jobject thiz, jint) 就是(I)Z

jint (JNIEnv* env  , jobject thiz, jint , jstring) 就是 (ILjava/lang/String;)I

void (JNIEnv* env  , jobject thiz, jint, jint) 就是(II)V

methods[]的第三个参数是固定写法,(void *)方法名

这样,一个完整的c文件就写好了,下面写Android.mk文件,用来编译,如下:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE    := libwdjni_devs
LOCAL_SRC_FILES := file.c
LOCAL_CFLAGS += -Wno-unused-const-variable -Wno-unused-variable -Wno-unused-parameter 
LOCAL_LDLIBS    := -llog 
include $(BUILD_SHARED_LIBRARY)

接着就该写调用的Java类了,关键代码(MainActivity.java)如下:

static {
        System.loadLibrary("wdjni_devs");/*加载JNI库*/
}

public native String stringFromJNI();

如此一来,就可以使用jni方法了。

Java的Android.mk如下:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)
LOCAL_AAPT_INCLUDE_ALL_RESOURCE := true

LOCAL_MODULE_TAGS := optional

LOCAL_SRC_FILES := $(call all-java-files-under,src)

LOCAL_AAPT_FLAGS := \\
    --auto-add-overlay \\
    --extra-packages android.support.v7.appcompat \\
	
LOCAL_STATIC_JAVA_LIBRARIES := \\
	android-support-v4 \\
    android-support-v7-appcompat \\

LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_RESOURCE_DIR += prebuilts/sdk/current/support/v7/appcompat/res

LOCAL_JNI_SHARED_LIBRARIES := libwdjni_devs

#LOCAL_REQUIRED_MODULES := wdjni_devs

LOCAL_PACKAGE_NAME := MyApp
LOCAL_CERTIFICATE := platform

LOCAL_PRIVATE_PLATFORM_APIS := false

LOCAL_DEX_PREOPT := false

include $(BUILD_PACKAGE)

include $(call all-makefiles-under,$(LOCAL_PATH))

好了,这样的话,就可以通过mmm,来编译出apk和so库了。

就写到这儿吧!如果你有什么疑问,欢迎留言跟我讨论哈~

以上是关于从零开始编写JNI的主要内容,如果未能解决你的问题,请参考以下文章

从零开始编写JNI

从零开始编写JNI

Android之从零开始JNI研发

Android之从零开始JNI研发

Android之从零开始JNI研发

从零开始配置vim(27)——代码片段