JNI学习,不通过h头文件调用Jni的demo

不通过头文件,那通过啥?
答案是:通过 注册本地函数的方法来实现jni的调用,这种方式更适用于在 源码环境编译

相关环境
  • ubuntn14.04 (64位)
  • 源码环境:full_z703_32-eng(全编)
  • 编译所需要的文件,如截图:
其中,src文件夹是java代码,res文件夹是资源文件夹,结构跟Android studio项目的一致。
jni文件夹里面放的就是c,c++的源码文件,当你需要调用开源的c,c++代码时,也是放在这个文件夹里面。同时还需要在这个文件夹里编写Android.mk文件来把c,c++源码编译成库。
  • jni文件夹的截图:
其中,选中的两个文件夹是关键,native3.cpp用来实现java本地方法,Android.mk文件用来指导编译出库文件。其他的都是开源的memtester的代码。

第一步,java层面
  • 在java代码中声明一个类,并声明一个本地方法,同时加载库。

class Native
{
    static {
        System.loadLibrary("simplejni2");//加载库
    }
    static native int dotest(String size,String loopnum);//声明一个本地方法
}



第二步,c、c++层面
  • 在c++代码中去实现这个本地函数
//可以看到该函数跟在java代码声明的格式有点区别,但是也是一一对应起来的
static jint
dotest(JNIEnv *env, jobject thiz, jstring size, jstring loopnum) {
    return 0;
}



  • 在c++代码中去调用注册本地函数的函数(注意,这里必须是c++代码才行)
步骤简介:
  1. 首先要指定好声明本地函数的java的类名
  2. 然后要写好函数签名的格式
  3. 声明并实现注册本地函数的函数
  4. 在JNI_OnLoad函数里调用上述(3)的本地注册函数(JNI_OnLoad函数会在java代码加载库的时候被调用)
  5. 写好Android.mk文件
  6. 再次明确哪些变量需要写对!!!

步骤1详解:
代码:
static const char *classPathName = "com/guohao/testcharforjni/Native2";

步骤2详解:
函数签名,不同的参数和返回值需要有对应的写法,
参考: http://blog.csdn.net/conowen/article/details/7524744
对应:static jint dotest(JNIEnv *env, jobject thiz, jstring size, jstring loopnum)函数
本地函数签名的 代码写法:
// Ljava/lang/String;Ljava/lang/String;对应的是两个String的参数
// I对应的是返回的int类型
static JNINativeMethod methods[] = {"dotest", "(Ljava/lang/String;Ljava/lang/String;)I", (void*)dotest };

步骤3详解:
前两个步骤写好了,这一步的代码都是一个模板。
这函数的作用就是注册本地函数,然后将会在步骤4的 JNI_OnLoad函数里被调用。
代码:套路型的,可直接复制
/*
 * Register several native methods for one class.
 */
static int registerNativeMethods(JNIEnv* env, const char* className,
    JNINativeMethod* gMethods, int numMethods)
{
    jclass clazz;

    clazz = env->FindClass(className);
    if (clazz == NULL) {
        ALOGE("Native registration unable to find class '%s'", className);
        return JNI_FALSE;
    }
    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
       ALOGE("RegisterNatives failed for '%s'", className);
        return JNI_FALSE;
    }

    return JNI_TRUE;
}

/*
 * Register native methods for all classes we know about.
 * returns JNI_TRUE on success.
 */
static int registerNatives(JNIEnv* env)
{
  if (!registerNativeMethods(env, classPathName,
                 methods, sizeof(methods) / sizeof(methods[0]))) {
    return JNI_FALSE;
  }
  return JNI_TRUE;
}



步骤4详解:
该步骤要实现的JNI_OnLoad函数,将会被 第一步,java层面 的代码:
System.loadLibrary("memtester");// 加载库
执行的时候调用。
相关代码:套路型的,可直接复制
/*
 * This is called by the VM when the shared library is first loaded.
 */ 
typedef union {
    JNIEnv* env;
    void* venv;
} UnionJNIEnvToVoid;

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    UnionJNIEnvToVoid uenv;
    uenv.venv = NULL;
    jint result = -1;
    JNIEnv* env = NULL;
    
    ALOGI("JNI_OnLoad");

    if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {
        ALOGE("ERROR: GetEnv failed");
        goto bail;
    }
    env = uenv.env;
    if (registerNatives(env) != JNI_TRUE) {
        ALOGE("ERROR: registerNatives failed");
        goto bail;
    }    
    result = JNI_VERSION_1_4;    
bail:
    return result;
}



步骤5详解:
Android.mk文件的作用,告诉编译器要讲哪些文件编译,编译成什么类型的。
代码:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
#要将谁编译
LOCAL_SRC_FILES:= memtester.c tests.c native3.cpp
#要编译成什么
LOCAL_MODULE:= libsimplejni2
#引入哪些共享库
LOCAL_SHARED_LIBRARIES := \
	libutils liblog
#引入哪些头文件
LOCAL_C_INCLUDES += \
	$(JNI_H_INCLUDE)
# No special compiler flags. 不太懂有啥用
LOCAL_CFLAGS += -fpermissive -Wwrite-strings -Wparentheses 
#编译成共享库
include $(BUILD_SHARED_LIBRARY)



步骤6详解:
在编写这些代码文件的过程中,很容易混淆一些变量的对应值。
现在来总结一下:
围绕着库这个中心来展开,
首先编译生成的库名在jni/Android.mk文件指定:LOCAL_MODULE:= libsimplejni2
引入这个库的代码就在java代码中指定:System.loadLibrary("simplejni2");// 加载库

其次,在c++注册本地函数时,要指定java声明的本地类
c++代码:static const char *classPathName = "com/guohao/testcharforjni/Native2";
java声明的代码:
class Native
{ 。。。 }

最后,{项目路径}/Android.mk文件还要指定生成的apk的名字:
LOCAL_PACKAGE_NAME := MemTestDemo


你可能感兴趣的:(JNI学习,不通过h头文件调用Jni的demo)