JNI初步使用



最近写了一个网站模拟点击的项目,究其原因是合作方提供不了接口。利用httpclient模拟浏览器访问,深深的感到这事的蛋疼性。然后蛋疼的事还不止这个,做的是一个远在千里之外内网的一个环境,远程调试又卡的要命。经过漫长的搜集数据和尝试,这个接口项目总算是结束了。 公司考虑到提供出去的接口一反编译就暴露在阳光下了,所有才有了JNI。


废话不说了,进入正题。

Jni程序开发的一般操作步骤如下:

l       编写java中的调用类

l         用javah生成c/c++原生函数的头文件

l         c/c++中调用需要的其他函数功能,实现原生函数(原则上可以调用任何资源)

l         将项目依赖的所有原生库和资源加入到java项目的java.library.path

l         生成java程序

l         发布java应用和dll或者so


首先,建立一个测试的类

package com.ist.manage.ess;          											       
 public class BssSupport  {
 	static {
 	 System.loadLibrary("BssSupport");
 	}
 
   public static void main(String[] args) {
       
         BssSupport bs = new BssSupport();
         bs.test("hello,java");
     }
   
   
   public void  imi(String s)
   {
    System.out.println(s);
   }
 public  native void test(String instring);										           }

 打开cmd进入到工程的目录下 
 

javah -encoding UTF-8 -jni com.ist.manage.ess.TestHello  //因为项目本来是utf8编码的缘故所有加上utf8,产生一个.h文件

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
 
#ifndef _Included_com_ist_manage_ess_TestHello
#define _Included_com_ist_manage_ess_TestHello

#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_ist_manage_ess_TestHello
 * Method:    test
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL  Java_com_ist_manage_ess_BssSupport_test(JNIEnv *, jobject,jstring );
#ifdef __cplusplus
}
#endif
#endif

观察一下就能得出函数是Java +报名 +函数名组成,中间中下划线分割,如果函数名中包含下划线就会被特殊处理,所以还是不要用那么蛋疼的命名规则,其实也很简单,再说java的函数名,一般不加下划线的,注意一下。

还看到了生僻的类型,这些类型是jni特有的和C++有一一对应的关系。

    Java类型                 本地类型                  JNI中定义的别名    
int long jint
long _int64 jlong
byte signed char jbyte
boolean unsigned char jboolean
char unsigned short jchar
short short jshort
float float jfloat
double double jdouble
Object _jobject* jobject

 


需要在jdk目录下

在%JAVA_HOME%/include/下找到 jni.h,

在%JAVA_HOME%/include/win32/下找到jni_md.h,

可以复制到vs/vc的include/”下,这样就不用费力的在工程中导入这两文件了。

vc不能编译出64位的dll,果断用了vs2010,发现不支持C99这也是一件坑爹的地方。

vs建工程很简单,新建一个空的dll工程即可,省的输入一大堆命令编译。


建一个.cpp文件,这里代码很简单,先包含头文件,然后通过jni提供的函数调用java函数imi()

#include "com_ist_manage_ess_TestHello.h"
JNIEXPORT void JNICALL  Java_com_ist_manage_ess_BssSupport_test(JNIEnv *env, jobject obj,jstring in_url)
{
	jclass class_Test =env->GetObjectClass(obj);
	jmethodID mid = env->GetMethodID(class_Test, "imi","(Ljava/lang/String;)V" );
	env->CallObjectMethod(obj, mid,in_url);
	
}
这里比较蛋疼的问题是jni签名,为什么会有这个东西,以为java支持函数重载,编译器必须知道函数的形参类型才能到找到具体的函数。

然后,得说明一下这些函数参数和签名的对应关系才好下手。

类型标示

Java类型

类型标示

Java类型

Z

boolean

F

float

B

byte

D

double

C

char

Ljava/lang/String

String

S

short

[I

Int[]

I

int

[Ljava/lang/object

Object[]

J

long

上面表格中列出了常用的类型标示,如果java类型是数组,则标示中会出现一个“[”,另外引用类型(除基本类型的数组外)的标识最后都有一个”;”分号

函数签名一开始最好自己手动输入理解一下熟练后可以利用java自带的工具自动生成

javap -s -p xxx

Xxx为编译后的class文件,s标识输出内部数据类型的签名信息,p表示打印所有的函数和成员的签名信息,默认只会打印public成员和函数的签名信息


类的签名规则是:“L+全限定类名+;” 三部分组成,例如:

  
  
  
  
  1. long fun (int n, String str, int[] arr); 

  
  
  
  
  1. (ILjava/lang/String;[I)J 

括号里面的内容分成三部分,之间没有空格,

即“I”、“Ljava/lang/String;”和“[I”,

分别代表 int、String和int[]。括号外面是返回值类型签名,J代表long型。


这里还有必要解释一下参数1和参数2 ,参数1就是jni环境了,可以用一张图表示。参数2就是调用JNI的那个对象了,如果是静态方法变对应的类。写一个静态的函数,javah一下就看到了参数名就不叫object了,但是类型都是一样的。

相信代码一看就懂,编译一下把生产的.dll扔到 c:/window/systm32目录下(path自己加也太麻烦了,就干脆用这个目录了)

然后运行java程序就看到了效果。










 




你可能感兴趣的:(c,jni)