Android Jni中JNI_OnLoad的地位和使用


在jni中,或者android系统源代码中,最常用到的是通过JNI_OnLoad(...)方法,对方法进行注册,而不会像前面的测试demo,生成.h头文件,然后写一个.c的去实现.h中的方法,JNI_OnLoad的引入更加注重c++的使用,至少偏向c++,android系统源代码也是如此,到了下面往往是c++做了一个过渡,但是再进一步深入,最终还是c实现的,比如网络协议等,这一点可以看出实际上用到了c++面向对象编程对c做了封装,然后提供给java,而java也是面向对象的语言,对于衔接起了非常重要的作用.

在应用层加载so的时候,虚拟机首先回去/自动执行JNI_OnLoad(...),所以在这里注册所有的方法/函数就好了,加载到虚拟机中,等待上面调用!

下面验证一下,新建一个android工程如下:

<1> : 新建android工程,目录树如下:

Android Jni中JNI_OnLoad的地位和使用_第1张图片

<2> : 下面的文件内容如下:

DurianOnLoad.java :

/**  
* @Title: DurianOnLoad.java
* @Package com.durian.jnionload.lib
* @Description: TODO
* @author zhibao.liu from durian organization
* @date 2015-12-25 下午04:46:13
* @version V1.0  
*/
package com.durian.jnionload.lib;

/**
 * @ClassName: DurianOnLoad
 * @Description: TODO
 * @author zhibao.liu Freelancer
 * @email [email protected]
 * @date 2015-12-25 下午04:46:13
 *
 */
public class DurianOnLoad {

    public native float getDurianLibVersion();
    public native int DurianAdd(int a,int b);
    public native int DurianEqual(int a);
    public native float DurianEqualF(float a,float b);
    static{
        System.loadLibrary("durianjni");
    }
    
}

durianUtils.h

#include 
/* Header for class com_durian_jnienv_lib_DurianJni */

#ifndef _Included_com_durian_jnienv_lib_DurianUtils
#define _Included_com_durian_jnienv_lib_DurianUtils
#ifdef __cplusplus
extern "C" {
#endif
#include
#include
#include 
#define LOG_TAG "durian"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,LOG_TAG,__VA_ARGS__)

extern char* Jstring2CStr(JNIEnv* env, jstring jstr);

#ifdef __cplusplus
}
#endif
#endif

durianUtils.c

#include
#include
#include "durianUtils.h"

char* Jstring2CStr(JNIEnv* env, jstring jstr) {
    char* rtn = NULL;
    jclass clsstring = (env)->FindClass( "java/lang/String");
    jstring strencode = (env)->NewStringUTF("GB2312");
    jmethodID mid = (env)->GetMethodID(clsstring, "getBytes",
            "(Ljava/lang/String;)[B");
    jbyteArray barr = (jbyteArray)(env)->CallObjectMethod(jstr, mid,
            strencode); // String .getByte("GB2312");
    jsize alen = (env)->GetArrayLength(barr);
    jbyte* ba = (env)->GetByteArrayElements(barr, JNI_FALSE);
    if (alen > 0) {
        rtn = (char*) malloc(alen + 1); //new   char[alen+1]; "\0"
        memcpy(rtn, ba, alen);
        rtn[alen] = 0;
    }
    (env)->ReleaseByteArrayElements(barr, ba, 0); //释放内存

    return rtn;
}


jni_onload.h

#include
#ifndef _ON_LOAD_HEADER_H__
#define _ON_LOAD_HEADER_H__
#ifdef __cplusplus
extern "C" {
#endif

#include "durianUtils.h"

JNIEnv* getJNIEnv();
int jniThrowException(JNIEnv *env,const char* className,const char* msg);
int jniRegisterNativeMethods(JNIEnv* env,const char* className,const JNINativeMethod* gMethod,int numMethods);

#ifdef __cplusplus
}
#endif
#endif


jni_onload.c

#include "jni_onload.h"
#include "durian_onload.h"

extern int register_android_jni_durian_android(JNIEnv *env);
/*
extern int register_android_jni_durian_android_1(JNIEnv *env);
...
extern int register_android_jni_durian_android_n(JNIEnv *env);
*/

static JavaVM *sEnv;

int jniThrowException(JNIEnv *env, const char* className, const char* msg) {

    jclass exceptionClass = env->FindClass(className);

    if (exceptionClass == NULL) {
        return -1;
    }

    if (env->ThrowNew(exceptionClass, msg) != JNI_OK) {

    }

    return 0;

}

JNIEnv* getJNIEnv() {

    JNIEnv* env = NULL;

    if (sEnv->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        return NULL;
    }

    return env;

}

int jniRegisterNativeMethods(JNIEnv* env, const char* className,
        const JNINativeMethod* gMethods, int numMethods) {
    jclass clazz;

    clazz = env->FindClass(className);

    if (clazz == NULL) {
        return -1;
    }

    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
        return -1;
    }

    return 0;

}

jint JNI_OnLoad(JavaVM* vm, void* reserved) {

    JNIEnv* env = NULL;
    jint result = JNI_ERR;
    sEnv = vm;

    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        return result;
    }

    LOGI("JNI_OnLoad...");
    /*ready to register all methods
    */

    if (register_android_jni_durian_android(env) != JNI_OK) {
        goto end;
    }

    //following could do it as the previous the way of register

    /*if (register_android_jni_durian_android_1(env) != JNI_OK) {
            goto end;
        }
    ...
    if (register_android_jni_durian_android_n(env) != JNI_OK) {
            goto end;
        }*/


    return JNI_VERSION_1_4;

    end: return result;

}


durian_onload.c

#include"jni_onload.h"
#include "durian_onload.h"

static const char *classpath = "com/durian/jnionload/lib/DurianOnLoad";
namespace android {

	float getDurianLibVersion() {
		return 12.5;
	}

	jint DurianAdd(JNIEnv *env, jobject thiz, jint a, jint b) {
		LOGI("a : %d b : %d",a,b);
		return (int)(a+b);
	}

	int DurianEqual(JNIEnv *env, jobject thiz,int eu){
		return eu*2;
	}

	float DurianEqualF(JNIEnv *env, jobject thiz,float ab,float cd){
		return ab+cd;
	}

}

using namespace android;

/*typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;

第一个变量name是Java中函数的名字。
第二个变量signature,用字符串是描述了函数的参数和返回值
第三个变量fnPtr是函数指针,指向C函数。
其中比较难以理解的是第二个参数,例如

"()V"
"(II)V"
"(Ljava/lang/String;Ljava/lang/String;)V"

实际上这些字符是与函数的参数类型一一对应的。
"()" 中的字符表示参数,后面的则代表返回值。例如"()V" 就表示void Func();
"(II)V" 表示 void Func(int, int);*/

static JNINativeMethod mMethods[] = {

		{ "getDurianLibVersion", "()F",(void *) getDurianLibVersion },
		{ "DurianAdd", "(II)I",(void *) DurianAdd },
		{ "DurianEqual","(I)I",(void *) DurianEqual},
		{ "DurianEqualF","(FF)F",(void *) DurianEqualF}

};

int register_android_jni_durian_android(JNIEnv* env) {

	return jniRegisterNativeMethods(env, classpath, mMethods,sizeof(mMethods) / sizeof(mMethods[0]));

}


durian_onload.h这个倒是不是必须的,只是方便查询Signature信息:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
/* Header for class com_durian_jnionload_lib_DurianOnLoad */

#ifndef _Included_com_durian_jnionload_lib_DurianOnLoad
#define _Included_com_durian_jnionload_lib_DurianOnLoad
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_durian_jnionload_lib_DurianOnLoad
 * Method:    getDurianLibVersion
 * Signature: ()F
 */
JNIEXPORT jfloat JNICALL Java_com_durian_jnionload_lib_DurianOnLoad_getDurianLibVersion
  (JNIEnv *, jobject);

/*
 * Class:     com_durian_jnionload_lib_DurianOnLoad
 * Method:    DurianAdd
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_durian_jnionload_lib_DurianOnLoad_DurianAdd
  (JNIEnv *, jobject, jint, jint);

#ifdef __cplusplus
}
#endif
#endif


Android.mk :

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_SHARED_LIBRARY := libnativehelper

LOCAL_MODULE :=durianjni
LOCAL_SRC_FILES := jni_onload.cpp
LOCAL_SRC_FILES += durianUtils.cpp
LOCAL_SRC_FILES += durian_onload.cpp

LOCAL_LDLIBS := -llog

include $(BUILD_SHARED_LIBRARY)


最后主要Android程序如下:

package com.durian.jnionload;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;

import com.durian.jnionload.lib.DurianOnLoad;

public class DurianMainActivity extends Activity {

    private final static String TAG="DurianMainActivity";
    
    private DurianOnLoad mDurianOnLoad;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.durian_main);
        
        mDurianOnLoad=new DurianOnLoad();
        float version=mDurianOnLoad.getDurianLibVersion();
        
        int sum=mDurianOnLoad.DurianAdd(1, 12);
        float versions=mDurianOnLoad.getDurianLibVersion();
        int eu=mDurianOnLoad.DurianEqual(10);
        float ef=mDurianOnLoad.DurianEqualF(10.0f,23.0f);
        Log.i(TAG,"sum : "+sum +" versions : "+versions+" eu : "+eu+" ef : "+ef);
        
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.durian_main, menu);
        return true;
    }

}


<3> :上面很奇怪,我记得我以前在linux下编译so的时候,不需要将方法写成如下形式,如下:

int DurianEqual(JNIEnv *env, jobject thiz,int eu){
		return eu*2;
}

只需要写成真正纯c/c++语言的:

int DurianEqual(int eu){
		return eu*2;
}

即函数前面两个参数不需要,今天奇怪了,难道是在windows下和linux下编译环境有差异的结果吗?后面我再到linux下面去验证一下,看来windows下做这样的project还是挺奇葩的.


运行上面的结果:
























你可能感兴趣的:(Android,NDK开发,手稿)