HAL/JNI简明笔记(三)——java如何识别native方法的实现

在文章HAL/JNI简明笔记(二)——基于stub架构的HAL实例,我们看见java调用jni导出接口是通过System.loadLibrary加载jni库,再声明下native即可,那么实现jni库的代码需要按照什么规则才能被JVM识别呢?

方法一,规范JNI函数名

方法二,通过jniRegisterNativeMethods来注册

不管用哪个方法,最终的目的就是在JVM中形成C函数和java方法的映射。


方法一的例子如下:

JNI->jni库

com_seuic_scanner_scanled.c

被Android.mk生成库libScannerled.so,放在/system/lib下他确实是库,不是stub,借尸so的stub一般放在/system/lib/hw下.

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include <jni.h>

#define SCANNER_LED_PATH "/sys/class/leds/scan/brightness"

static int SetScannerLed(int brightness)
{
	int fp;
	int size;
	char state = brightness?'1':'0';
	
	fp = open(SCANNER_LED_PATH,O_WRONLY);
	if(fp == -1)
		return -1;
	size = write(fp,&state,1);
	if(size > 0){
		close(fp);
		return 0;
	}
	close(fp);
	return 0;
}

void timer_handler(int msg)
{
	switch(msg)
	{
		case SIGALRM:
			SetScannerLed(0);
			break;
		default:
			break;
	}
}


JNIEXPORT jint JNICALL Java_com_seuic_scanner_ScanLed_JNISetScanled(JNIEnv *env, jobject obj, jint brightness)
{
	int ret = -1;
	struct itimerval nvalue;

	ret = SetScannerLed(brightness);
	if (ret < 0)
		return ret;
	signal(SIGALRM, timer_handler);
	nvalue.it_value.tv_sec = 0;
	nvalue.it_value.tv_usec = 100000;//100ms
	nvalue.it_interval.tv_sec = 0;
	nvalue.it_interval.tv_usec = 0;
	setitimer(ITIMER_REAL, &nvalue, NULL);

	return ret;
}



这个文件只导出了一个函数 ScanLed_JNISetScanled,但函数名为什么写的那么复杂呢?

JVM是按照一定的命名规则来搜索导出的native函数的,这个规则就是函数命名规则:Java_包名_类名_方法名,其中包名中的点也用下划线'_'替代。上例中解释为com.seuic.scanner包中ScanLed类的方法为JNISetScanled,这个方法JNISetScanled也是java中使用的名字,这样JVM以后就一直用这样的函数映射。JNIEnv*env和jobject obj两个参数是必须要有的。

那么JNIEXPORT和JNICALL是干什么的呢?

其实是不同平台之间的兼容性需求,他们中间的jint是返回值类型。

JAVA_HOMEinclude/x/jni.hinclude/x/jni_md.h文件中的内容(x stands for win32 or linux,or any other OS name),都是一些简单的宏定义。关于一般上面代码一些陌生的东西的定义在Java安装路径里的include中,Windows系统上是win32文件夹;Linux系统上是linux文件夹。
win32/jni_md.h文件里

#define JNIEXPORT __declspec(dllexport)
#define JNIIMPORT __declspec(dllimport)
#define JNICALL __stdcall

linux/jni_md.h文件里

#define JNIEXPORT
#define JNIIMPORT
#define JNICALL

如上可见在linux环境就是空定义的,所以在linux下偏驱动的人就不用关心了,即使去掉JNIEXPORT和JNICALL也不影响;如果你是在windows(一般为偏apps的开发人员)下开发就加上吧,为了保持良好的兼容性都加上比较稳妥。

说明:层次简单代码量减少,它的编程思路一般是先在java中定义好方法,然后利用javah把带有native声明的函数生成.h头文件,再去实现c/c++文件,这个方法一般来自于apps层次的人员用,当然既然你都知道命名规则了,你直接先写c文件,再去写java文件也可以,当代码量大的时候,名字会让你头疼。


方法二例子:

JNI->jni库->so形式的stub

HAL/JNI简明笔记(二)——基于stub架构的HAL实例,我仅贴出jni代码部分

namespace android  
{  
// These values must correspond with the Mode constants in  
// CTPService.java  
enum {  
    MODE_GLOVE_OFF = 0,  
    MODE_GLOVE_ON = 1,  
};  
  
static ctp_device_t* get_device(hw_module_t* module, const char* name)  
{  
    int err;  
    hw_device_t* device;  
    err = module->methods->open(module, name, &device);  
    if (err == 0) {  
        return (ctp_device_t*)device;  
    } else {  
        return NULL;  
    }  
}  
  
/*return !NULL when OK,or NULL*/  
static jint open(JNIEnv *env, jobject clazz)  
{  
    int err;  
    hw_module_t* module;  
    ctp_device_t* dev = NULL;  
  
    err = hw_get_module(CTP_HARDWARE_MODULE_ID, (hw_module_t const**)&module);  
    if (err == 0) {  
        dev = get_device(module, CTP_NAME);  
    }  
  
    return (jint)dev;  
}  
  
static void close(JNIEnv *env, jobject clazz, int ptr)  
{  
    ctp_device_t* dev = (ctp_device_t*)ptr;  
  
    if (dev) {  
        dev->common.close((struct hw_device_t*)dev);  
    }  
}  
  
static jstring GetCtpVer(JNIEnv *env, jobject clazz, int ptr)  
{  
    char ver[15];  
    memset(ver, 0 , sizeof(char) * 15);  
    ctp_device_t* dev = (ctp_device_t*)ptr;  
    if (!dev) {  
        return NULL;  
    }  
  
    dev->GetCtpVer(dev,(char *)ver);  
    return env->NewStringUTF(ver);  
}  
    
static JNINativeMethod method_table[] ={  
    { "open", "()I", (void*)open },  
    { "close", "(I)V", (void*)close },  
    { "GetCtpVer", "(I)Ljava/lang/String;", (void*)GetCtpVer},  
};  
int register_CTPService(JNIEnv *env)  
{  
    return jniRegisterNativeMethods(env, "com/seuic/touch/TouchService",  
        method_table, NELEM(method_table));  
}  
  
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)  
{  
    JNIEnv* env = NULL;  
    jint result = -1;  
  
    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {  
        ALOGE("GetEnv failed!");  
        return result;  
    }  
    ALOG_ASSERT(env, "Could not retrieve the env!");  
  
    register_CTPService(env);  
  
    return JNI_VERSION_1_4;  
}  
    
}//end of namespace android 
方法二在你在java中加载库后,会自动去调用名为JNI_OnLoad( )的函数,这个函数返回JNI_VERSION_1_4是告诉JVM用JNI 1.4版本,不要用老版本。函数体中的register_CTPService(env)最终调用jniRegisterNativeMethods,这就是注册native到JVM,形成永久的java和c函数之间的映射关系,对比上列中参数一固定为env,参数二为注册到的com.secuic.touch包中的TouchService类中,参数三为注册的native方法映射表指针,参数四为映射表中个数。映射表每个元素的结构体定义在jni.h如下,
typedef struct { 
const char* name; 
const char* signature; 
void* fnPtr; 
} JNINativeMethod; 
每个映射的参数一为java中用的方法名,参数二为字符串表示的输入输出参数类型,参数三为C中的函数名。

关于映射表的输入输出参数表示,详细见oracle网站说明

http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html

JNI中参数一般分为基本类型和引用类型

Java 类型

Type Signature

本地类型

描述

boolean

Z

jboolean

C/C++8位整型

byte

B

jbyte

C/C++带符号的8位整型

char

C

jchar

C/C++无符号的16位整型

short

S

jshort

C/C++带符号的16位整型

int

I

jint

C/C++带符号的32位整型

long

J

jlong

C/C++带符号的64位整型e

float

F

jfloat

C/C++32位浮点型

double

D

jdouble

C/C++64位浮点型


Object

 


jobject


任何Java对象,或者没有对应java类型的对象

Class

Ljava/lang/Class;

jclass

Class对象

String

Ljava/lang/String;

jstring

字符串对象

Object[]

 

jobjectArray

任何对象的数组

boolean[]

[Z

jbooleanArray

布尔型数组

byte[]

[B

jbyteArray

比特型数组

char[]

[C

jcharArray

字符型数组

short[]

[S

jshortArray

短整型数组

int[]

[I

jintArray

整型数组

long[]

[J

jlongArray

长整型数组

float[]

[F

jfloatArray

浮点型数组

double[]

[D

jdoubleArray

双浮点型数组


其中引用类型的signature表示方法为Lfully-qualified-class;,注意最后有个分号,数组的signature表示方法为[type。另外特别注意jchar不是C中的char了,是16bits无符号,一般给unicode用的,而对应于char的脚jbyte.

引用类型tree,



ref: xyang0917  JNI/NDK开发指南专题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值