android jni中碰到的问题 和步骤 (C 和 java互调)

  • Post author:
  • Post category:java



在C/C++中调用Java的方法一般分为五个步骤:初始化虚拟机、获取类、获取类的方法、创建类对象、调用方法和退出虚拟机。



以下摘自:http://blog.csdn.net/sunchaoenter/article/details/6598719


Java代码:



  1. package


    jni.test;




  2. public




    class


    Demo {




  3. public




    static




    int


    COUNT =


    8


    ;




  4. public


    String msg;



  5. private




    int


    [] counts;




  6. public


    Demo() {



  7. this


    (


    “缺省构造函数”


    );


  8. }



  9. public


    Demo(String msg) {


  10. System.out.println(

    “<init>:”


    + msg);



  11. this


    .msg = msg;



  12. this


    .counts =


    null


    ;


  13. }



  14. public


    String getMessage() {



  15. return


    msg;


  16. }



  17. public




    int


    [] getCounts() {



  18. return


    counts;


  19. }



  20. public




    void


    setCounts(


    int


    [] counts) {



  21. this


    .counts = counts;


  22. }



  23. public




    void


    throwExcp()


    throws


    IllegalAccessException {



  24. throw




    new


    IllegalAccessException(


    “exception occur.”


    );


  25. }

  26. }

上面的代码很好理解,我相信你能看的懂,我就不说了。

下面是C语言代码,里面有注释,这里先不详细说明,主要先跑起来再说:



  1. #include <stdio.h>





  2. #include <jni.h>





  3. #include <stdlib.h>





  4. #include <iostream.h>





  5. int


    main() {



  6. // 定义用到的变量





  7. int


    res;


  8. JavaVM *jvm;

  9. JNIEnv *env;

  10. JavaVMInitArgs vm_args;

  11. JavaVMOption options[3];

  12. vm_args.version;



  13. // 设置初始化参数




  14. options[0].optionString =

    “-Djava.compiler=NONE”


    ;



  15. // classpath有多个时,用”;”分隔,UNIX下以”:”分割。




  16. options[1].optionString =

    “-Djava.class.path=.”


    ;



  17. // 用于跟踪运行时的信息




  18. options[2].optionString =

    “-verbose:jni”


    ;



  19. // 版本号设置不能漏




  20. vm_args.version = JNI_VERSION_1_6;

  21. vm_args.nOptions = 3;

  22. vm_args.options = options;

  23. vm_args.ignoreUnrecognized = JNI_TRUE;


  24. // 1.初始化虚拟机




  25. res = JNI_CreateJavaVM(&jvm, (

    void


    **)&env, &vm_args);



  26. if


    (res < 0)


  27. {

  28. fprintf(stderr,

    “Can’t create Java VM\n”


    );


  29. exit(1);

  30. }


  31. // 2.获取类




  32. jclass cls = env->FindClass(

    “jni/test/Demo”


    );



  33. // 3.获取类的方法




  34. jmethodID mid= env->GetMethodID(cls,

    “getMessage”


    ,


    “()Ljava/lang/String;”


    );



  35. // 获取Java的构造方法




  36. jmethodID con=env->GetMethodID(cls,

    “<init>”


    ,


    “(Ljava/lang/String;)V”


    );


  37. jstring strinit = env->NewStringUTF(

    “Still is coding!”


    );


  38. jvalue arg[1];

  39. arg[0].l = strinit;


  40. //env->AllocObject(cls);





  41. // 4.创建类的对象




  42. jobject obj = env->NewObjectA(cls,con,arg);


  43. // 调用对象的方法




  44. jstring msg = (jstring)env-> CallObjectMethod(obj, mid);


  45. cout<<msg<<endl;


  46. char


    *str=(


    char


    *)env->GetStringUTFChars(msg,JNI_FALSE);


  47. printf(

    “%s===”


    ,str);



  48. // 5.退出虚拟机




  49. jvm->DestroyJavaVM();

  50. fprintf(stdout,

    “Java VM destory.\n”


    );



  51. return


    0;


  52. }

把以上C语言代码用VC6.0打开,然后编译,下面问题就来了,我们一个一个解决。

编译的时候首先会出现如下问题:

错误很明显,我们上面包含了jni.h

,但是没找到。这是需要设置一下


VC



Tools->Options->Directories,

添加


JDK


安装目录下的两个目录,如图:

同时在

Library files


中添加


JDK


下面的


LIB


目录,如下图:

点击

OK


完成,重新编译。

好,新问题又来了,看下面描述:

也很明显,说明没有找到

jvm.lib


。继续设置


VC





Project->Settings->Link->Object/library modules


中把上述路径替换为本机实际安装


JDK


中的


jvm.lib


目录,添加完后如下图:



点击

OK


后重新编译,呵呵,又来问题了,我很高兴啊。



这个问题在网上查了一下,原来是我

JDK


安装在


Program Files


下的问题,因为


Program Files


路径中有一个空格,真郁闷,没办法,只好重装


JDK


,注意安装目录中不能再有空格了。安装完了,按照上面出现的问题再重新设置一下


VC


,然后编译,没问题,运行,哇靠,又是一个问题:



这个问题可是花了我大半天时间才搞定的,但其实做法很简单。

看错描述,是没找到

jvm.dll


,网上很多人说直接找到这个文件把它拷贝到当前目录。但是这种方法不可取,因为


jvm.dll


这个东东还会依赖其他的文件的,而且它找其它所依赖的文件是通过相对路径找的,你直接就搞这么一个文件出来,其它的也找不到啊。所以这里有一个很好的解决方案,就是把


D:\Java\jdk1.6.0_23\jre\bin\client


这个


JDK


下的路径加入


Path


环境变量,


加完之后记得重新用



VC




打开




CPP




文件,这个很重要


,否则,这个问题还是没有解决。

点击编译,运行,如果出现如下画面,

OK


,恭喜你,


C


语言调用


Java


成功了,下面要做的就是去看代码,并且了解运行的机理了,这个我就不多说了。




java 调用 c/c++


1.使用native定义方法 本地方法



2.编译得到字节码文件



3.使用javah -jni xxx命令生成C语言头文件



4.C语言实现头文件定义的方法,再用ndk打包城.so文件



5.java程序中使用loadlibry方法加载库文件







问题找不到文件:






使用Javah 可以获取您的 Java 源文件并生成 C/C++头文件,其中包含您的 Java 代码中所有本地方法(native方法)的 JNI 存根(stub,C头文件)。如果您正在生成一个类的 JNI 存根,而且您已经把这个类定义为包的一部分,那么您 必须指定完全限定的类名。


下面举例说明:


使用eclipse建立一个工程假设工程路径为$ProjectPath,并且你已经定义了一个类,并且带包名:cn.com.comit.jni


—————————-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

package


cn.com.comit.jni;

public


class


HelloJni {





public


native


void


displayHelloJni();



static


{




System.loadLibrary(


""


);



}



/**



* @param args



*/



public


static


void


main(String[] args) {




// TODO Auto-generated method stub



new


HelloJni().displayHelloJni();



}

}


—————————-


eclipse会自动帮你编译出一个字节码文件HelloJni.class,路径是$ProjectPath\bin\cn\com\comit\jni,很可能你会先cd到.class的目录这么做:


>

cd $ProjectPath\bin\cn\com\comit\jni


>$ProjectPath\bin

javah HelloJni


发现执行出错:


错误:无法访问 HelloJni

错误的类文件: .\HelloJni.class

类文件包含错误的类: cn.com.comit.jni.HelloJni

请删除该文件或确保该文件位于正确的类路径子目录中。


看来是路径有问题咯。那改成


javah cn.com.comit.jni.HelloJni


再次运行,发现还是错误:


错误:无法访问 cn.com.comit.jni.HelloJni

未找到 cn.com.comit.jni.HelloJni 的类文件

javadoc: 错误 – 找不到类 cn.com.comit.jni.HelloJni。







如果是android工程需要用户切换的目录为

$ProjectPath\bin\classes\文件夹



怎么才能解决这个问题呢?其实只要cd到包的上一级目录(我们这里是$ProjectPath\bin)在运行下面的命令就搞定了:


javah -classpath . cn.com.comit.jni.HelloJni


看一下生成的C头文件:

1
2
3
4
5

/* DO NOT EDIT THIS FILE - it is machine generated */

#include <jni.h>

/* Header for class cn_com_comit_jni_HelloJni */

//避免重复包含头文件

1
2

#ifndef _Included_cn_com_comit_jni_HelloJni

#define _Included_cn_com_comit_jni_HelloJni

1

//c++编译环境中才会定义__cplusplus (plus就是"+"的意思

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

#ifdef __cplusplus

extern


"C"


{


//告诉编译器下面的函数是c语言函数(因为c++和c语言对函数的编译转换不一样,主要是c++中存在重载)

#endif

/*



* Class:     cn_com_comit_jni_HelloJni



* Method:    displayHelloJni



* Signature: ()V



*/

JNIEXPORT


void


JNICALL Java_cn_com_comit_jni_HelloJni_displayHelloJni



(JNIEnv *, jobject);

#ifdef __cplusplus

}

#endif

#endif


android.mk文件的简介:


http://www.cnblogs.com/luxiaofeng54/archive/2011/08/13/2137577.html



版权声明:本文为luoxiaozhuo原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。