Android开发:javah的使用方法

Android开发:javah的使用方法

标签: JNI  Android  javah
原创作品,允许转载,转载时请务必以超链接形式标明文章  原始出处 、作者信息和本声明。否则将追究法律责任。 http://whithin.blog.51cto.com/690417/1174645

通过javah可以自动生成相应的JNI的.h文件,运行“javah -help” 命令显示用法如下:

  
  
  1. Z:\>javah -help 
  2. Usage: javah [options] <classes> 
  3.  
  4. where [options] include: 
  5.  
  6.         -help                 Print this help message and exit 
  7.         -classpath <path>     Path from which to load classes 
  8.         -bootclasspath <path> Path from which to load bootstrap classes 
  9.         -d <dir>              Output directory 
  10.         -o <file>             Output file (only one of -d or -o may be used) 
  11.         -jni                  Generate JNI-style header file (default
  12.         -version              Print version information 
  13.         -verbose              Enable verbose output 
  14.         -force                Always write output files 
  15.  
  16. <classes> are specified with their fully qualified names (for instance, java.lang.Object). 

 

example:
 
hello-jni应用程序所在目录为 F:\hello-jni, 编译生成的class文件在bin\classes文件夹下,那么调用javah时的classpath 必须指向该路径
 
第一种情况:
F:\hello-jni>javah -classpath   bin/classes  com.example.hellojni.HelloJni 
在应用程序根目录下,那么classpath 的相对路径为 bin/classes
 
第二种情况:
F:\hello-jni\bin>javah -classpath   classes  com.example.hellojni.HelloJni
在bin目录下,那么classpath的相对路径为 classes
 
第三种情况:
F:\hello-jni\bin\classes>javah -classpath   .  com.example.hellojni.HelloJni
在classes目录下,那么classpath应该为当前目录,即 .
 
第四种情况:
F:\temp>javah -classpath   F:\hello-jni\bin\classes   com.example.hellojni.HelloJni
此时classpath 应该指向classes所在的绝对路径,即F:\hello-jni\bin\classes 

====


注意:遇到了ANT自动提示失灵,主要是ADT开发环境针对XML打开自动采用了ANDROID编辑器的缘故,最好点击文件右键选择Ant Editor即可。

1)需要配置NDK.选择采用AntEditor打开文件

2)需要指定默认,才能使用${basedir}自动提示,并且采用ALT+/功能。


<?xml version="1.0" encoding="UTF-8"?>
<!-- ====================================================================== 
     2013-7-8 上午10:41:39                                                        

     NDK-Hello    
     description
                   
     DELL                                                                
     ====================================================================== -->
<project name="NDK-Hello" default="gen">
    <description>
            description
    </description>

    <!-- ================================= 
          target: gen              
         ================================= -->
    <target name="gen" description="description">
        <exec dir="${basedir}/bin/classes/" executable="javah">
        	<arg value="-d"/>
        	<arg value="${basedir}/jni/"/>
        	<arg value="-jni"/>
        	<arg value="com.ndk.hello.NativeInterface"/>
       
        </exec>
    	
    </target>

</project>

双击编译成功。---------------

#include <jni.h>
#include "com_ndk_hello_NativeInterface.h"

/*
 * Class:     com_ndk_hello_NativeInterface
 * Method:    getName
 * Signature: ()I
 */
jint Java_com_ndk_hello_NativeInterface_getName
  (JNIEnv *, jobject){

	return 90000;
}

/*
 * Class:     com_ndk_hello_NativeInterface
 * Method:    getUserName
 * Signature: ()Ljava/lang/String;
 */
jstring Java_com_ndk_hello_NativeInterface_getUserName
  (JNIEnv * env, jobject){

	//利用JAVA运行环境。
	return env->NewStringUTF("hello android!这里是中文");
}


结果中文乱码,通过输出。
//-------------------------------------------------------------------
System.out.println(new NativeInterface().getName());
System.out.println(new NativeInterface().getUserName());

package com.ndk.hello;

public class NativeInterface {
	
	public native int getName();
	
	public native String getUserName();
	
	static {
		System.loadLibrary("hello");
	}
}


/=================

ant执行dos命令 
方法一: 
<target name="packClient"> 
    <echo message="客户端做包" /> 
    <delete> 
            <fileset dir="${Path_Packet}/Client/10x" includes="**/*.*"/> 
            <fileset dir="${Path_Packet}/Client/auto_lua" includes="*.pak"/> 
        </delete> 
        <exec executable="C:/WINDOWS/system32/cmd.exe"  failοnerrοr="false" dir="${Path_Packet}/Client/auto_lua"> 
                <arg line="/k for /d %a in (./*) do ${packfile} P -P%a.pak -R${Path_Packet}/Client/auto_lua -D%a" />             
            </exec>    
    </target> 

方法二: 
<target name="packClient"> 
    <echo message="客户端做包" /> 
    <exec executable="doit.bat"  failοnerrοr="false" dir="${Path_Packet}/Client/auto_lua">           
            </exec>    
    </target>

============================================

"${Path_Packet}“

"${BaseDir}"

===============

jni和C++通信中文乱码的问题

首先,需要明确几个关于编码的基本概念:

  • java内部是使用的16bit的unicode编码(utf-16)来表示字符串的,无论英文还是中文都是2字节;
  • jni内部是使用utf-8编码来表示字符串的,utf-8是变长编码的unicode,一般ascii字符是1字节,中文是3字节;
  • c/c++使用的是原始数据,ascii就是一个字节,中文一般是GB2312编码,用2个字节表示一个汉字。

jni的中文字符串处理

先从字符流的方向分别对java-->C++和C++-->java进行分析

  • java-->C++

这种情况下,java调用的时候使用的是utf-16编码的字符串,jvm把这个参数传递给jni,C++得到的输入是jstring,此时,可以利用jni提供的两种函数,一个是GetStringUTFChars,这个函数将得到一个UTF-8编码的字符串;另一个是GetStringChars这个将得到UTF-16编码的字符串。无论那个函数,得到的字符串如果含有中文,都需要进一步转化成GB2312的编码。

       String 
(UTF-16)
|
[java] |
-------------------- JNI 调用
[cpp] |
v
jstring
(UTF-16)
|
+--------+---------+
|GetStringChars |GetStringUTFChars
| |
v v
wchar_t* char*
(UTF_16) (UTF-8)
  • c/c++ –> java

jni返回给java的字符串,c/c++首先应该负责把这个字符串变成UTF-8或者UTF-16格式,然后通过NewStringUTF或者NewString来把它封装成jstring,返回给java就可以了。

       String 
(UTF-16)
^
|
[java] |
-------------------- JNI 返回
[cpp] |
jstring
(UTF-16)
^
|
+--------+---------+
^ ^
| |
|NewString |NewStringUTF
wchar_t* char*
(UTF_16) (UTF-8)

 

如果字符串中不含中文字符,只是标准的ascii码,那么用GetStringUTFChars/NewStringUTF就可以搞定了,因为这种情况下,UTF-8编码和ascii编码是一致的,不需要转换。

但是如果字符串中有中文字符,那么在c/c++部分进行编码转换就是一个必须了。我们需要两个转换函数,一个是把UTF8/16的编码转成GB2312;一个是把GB2312转成UTF8/16。

 

这里要说明一下:linux和win32都支持wchar,这个事实上就是宽度为16bit的unicode编码UTF16,所以,如果我们的 c/c++程序中完全使用wchar类型,那么理论上是不需要这种转换的。但是实际上,我们不可能完全用wchar来取代char的,所以就目前大多数应用而言,转换仍然是必须的。

具体的转换函数,linux和win32都有一定的支持,比如glibc的mbstowcs就可以用来把 GB2312编码转成UTF16,但是这种支持一般是平台相关的(因为c/c++的标准中并没有包括这部分),不全面的(比如glibc就没有提供转成 UTF8的编码),不独立的(linux下mbstowcs的行为要受到locale设置的影响)。所以我推荐使用iconv库来完成转换。

iconv库是一个免费的独立的编码转换库,支持很多平台,多种编码(事实上,它几乎可以处理我们所使用的所有字符编码),而且它的行为不受任何外部环境的影响。iconv在*nix平台上,基本上是缺省安装的。在win32平台上需要额外安装。

下面提供一个把GB2312编码的字符串转换成UTF8编码的示例

#include <iconv.h>
char* BytesToUtf8(string src, char* dst, int* nout)
{
size_t n_in = src.length();
size_t n_out = *nout;

iconv_t c = iconv_open("UTF-8", "GB2312");
if (c == (iconv_t)-1) {
cerr << strerror(errno) << endl;
return NULL;
}

 

char* inbuf = new char [n_in + 1];
if (!inbuf) {
iconv_close(c);
return NULL;
}

strcpy(inbuf, src.c_str());
memset(dst, 0, n_out);

char* in = inbuf;
char* out = dst;
if (iconv(c, &in, &n_in, &out, &n_out) == (size_t)-1) {
cerr << strerror(errno) << endl;
out = NULL;
}
else {
n_out = strlen(dst);
out = dst;
}

iconv_close(c);
*nout = n_out;
delete[] inbuf;

return out;
}

 

补充几点说明:

1、从jni的接口看,jni提供了UTF16和UTF8两个系列的字符串处理函数,但是由于jni的文档中说,jni的内部实现中是用UTF8作为字符串编码格式的,所以使用UTF8系列比较合适(NewStringUTF/GetStringUTFChars /ReleaseStringUTFChars)

2、使用iconv库的话,运行环境的设置对于编码转换是没有影响的,但是外层java程序对于字符串的解析依赖于运行环境的locale,所以设置正确的locale对于jni意义不大,但是对整个系统还是必要的。

 

以上是主要是说明使用第三方库去解决编码问题,针对仅在windows平台下,是可以使用windows提供的相关方法进行编码转换的。

使用一下方法可以将jstring转换为char*,主要用于在C++中接收java传递过来的参数时包含中文字符时使用。在转换过程中已经对编码进行了转换,可以正常返回出中文字符。

char * JStringToWindows(JNIEnv * pJNIEnv, jstring jstr)
{
    jsize len = pJNIEnv->GetStringLength(jstr);
    const jchar * jcstr = pJNIEnv->GetStringChars(jstr, NULL);
    int size = 0;
    char * str = (char *)malloc(len * 2 + 1);
    if ((size = WideCharToMultiByte(CP_ACP, 0, LPCWSTR(jcstr), len, str, len * 2 + 1, NULL, NULL)) == 0)
        return NULL;
    pJNIEnv->ReleaseStringChars(jstr, jcstr);
    str[size] = 0;
    return str;
}

注意:以上方法中返回的char*在使用过后需要delete释放,因为在编码转换过程中使用了malloc分配了内存,不释放会发生内存泄漏。

如果需要在C++中返回中文信息给java,则需要以下方法将char*转换成jstring。

jstring WindowsTojstring( JNIEnv* env, char* str )
{
    jstring rtn = 0;
    int slen = strlen(str);
    unsigned short * buffer = 0;
    if( slen == 0 )
        rtn = (env)->NewStringUTF(str ); 
    else
    {
        int length = MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, slen, NULL, 0 );
        buffer = (unsigned short *)malloc( length*2 + 1 );
        if( MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, slen, (LPWSTR)buffer, length ) >0 )
            rtn = (env)->NewString(  (jchar*)buffer, length );
    }
    if( buffer )
        free( buffer );
    return rtn;
}

根据前面的字符流的分析,和后面提供的转换方法,基本上可以解决jni中中文参数乱码的问题。

以上内容部分摘自网络。属于个人总结内容。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值