从零开始编写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的主要内容,如果未能解决你的问题,请参考以下文章