1分钟攻克NDK开发中javah不能反编译的有关问题

1分钟攻克NDK开发中javah不能反编译的问题

网上关于javah不能反编译出.h头文件的问题的资料本来就不多,而且大多似乎已经过时,给出的几种解决方法都没有切中要害。害得我苦苦找了一天,终于修成正果。特分享如下:

这是我最终的工程结构图,如果你是个生手,应当能从中多少了解点儿NDK工程的架构。

                            1分钟攻克NDK开发中javah不能反编译的有关问题             

下面的代码是含有本地方法声明的Java文件 HelloJni.java。

package com.example.testndk;


import android.app.Activity;
import android.widget.TextView;
import android.os.Bundle;


public class HelloJni extends Activity
{
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        /* Create a TextView and set its content.
         * the text is retrieved by calling a native
         * function.
         */
        TextView  tv = new TextView(this);
        tv.setText( stringFromJNI() );//stringFromJNI
        setContentView(tv);
    }

    /* A native method that is implemented by the
     * 'hello-jni' native library, which is packaged
     * with this application.
     */
    public native String  stringFromJNI();

    static {
        System.loadLibrary("hello-jni");
    }
}

可以看到HelloJni.java中声明的本地方法是stringFromJNI()。

其实当HelloJni.java文件写好保存后就被Eclipse自动编译生成了.class文件,放在了工程根目录下的/bin\classes\com\example\testndk目录下。

      你说什么?我在/bin\classes\com\example\testndk目录怎么没有看到class文件啊?

你当然看不到,在Eclipse中这种中间过程产生的文件被隐藏了,你看不见。当然,我们可以用别的方式看到它。方法:在DOS下进入该目录,键入DIR,回车,哇,你惊讶看到了下面的画面,“原来你藏这里呀,找得我好苦!”

1分钟攻克NDK开发中javah不能反编译的有关问题


看到了吧,第3行第1列,HelloJni.class静静地躺在那儿,与众多class文件一道,等着你去发现。

我们回到工程根目录TestNDK/,用javah进行反编译,当然编译成功了,毕竟这是我努力了一天的成果啊。仔细看看这个命令的结构和各部分的含义,看不懂没关系,后面我有讲:

1分钟攻克NDK开发中javah不能反编译的有关问题

反编译出来的头文件(com_example_testndk_HelloJni.h)可以再当前目录(TestNDK工程根目录)下找到,如下图所示:

    1分钟攻克NDK开发中javah不能反编译的有关问题


首先把命令放在这儿,仔细分析哦:

    

D:\Program Files\Android\workspace\TestNDK>javah -classpath "D:\Program Files\Android\SDK\platforms\android-19\android.jar";bin/classes com.example.testndk.HelloJni

    其中黑色字体是当前工作目录,也是工程根目录。

为了更好地理解javah命令及其各参数的含义,我把javah命令的信息粘贴过来了:

Javah命令说明:

生成实现本地方法所需的 C 头文件和源文件。C 程序用生成的头文件和源文件在本地源代码中引用某一对象的实例变量。.h 文件含有一个 struct 定义,该定义的布局与相应类的布局平行。该 struct 中的域对应于类中的实例变量。
头文件名以及在头文件中所声明的结构名都来源于类名。如果传给 javah 的类是在某个包中,则头文件名和结构名前都要冠以该包名。下划线 (_) 用作名称分隔符。
缺省情况下,javah 为每个在命令行中列出的类都创建一个头文件,且将该文件放在当前目录中。用 -stubs 选项创建源文件。用 -o 选项将所有列出类的结果串接成一个单一文件。
新的平台相关方法接口(Java 平台相关代码接口 (JNI))不需要头文件信息或 stub 文件。javah 仍可用于生成 JNI 风格的本地方法所需的本地方法函数原型。javah 在缺省情况下生成 JNI 风格的输出并将结果放在 .h 文件中。

选项
 
-d          设置 javah 保存头文件的目录,即我们想得到的.h文件的目录。
 
 
-classpath  指定 javah 用来查询类的路径。如果设置了该选项,它将覆盖缺省值或 CLASSPATH 环境变量。目录用分号分隔。

 -jni       生成 jni样式的头文件

回到之前反编译用到的命令D:\Program Files\Android\workspace\TestNDK>javah -classpath "D:\Program Files\Android\SDK\platforms\android-9\android.jar";bin/classes com.example.testndk.HelloJni

其中:

      黑色字体D:\Program Files\Android\workspace\TestNDK是工作目录,也是工程TestNDK的根目录;

    双引号括起的字符串是一个jar包的绝对路径"D:\Program Files\Android\SDK\platforms\android-9\android.jar",可以找到这个jar包,截图如下:

    1分钟攻克NDK开发中javah不能反编译的有关问题


然后是一个分号,分号后面的内容是bin/classes com.example.testndk.HelloJni,把它与前面所找到的HelloJni.class的路径  /bin\classes\com\example\testndk\HelloJni.class相比较,你发现了什么?

    对,它把com\example\testndk\这部分路径用点分格式表示成了包的名称,把class文件名HelloJni.class去掉了后缀并用“.”与包名com.example.testndk连接,形成了com.example.testndk.HelloJni。HelloJni.class路径的剩余部分bin\classes表示从当前目录到包的相对文件路径。这个相对路径与 前者以空格相连。

   现在你明白反编译命令中bin/classes com.example.testndk.HelloJni是什么意思了吧,其实就是当前目录(工程根目录)到class文件(HelloJni.class)的相对路径,只是换了种写法而已。大呼原来如此。

如果读懂了上面的的内容并且已经能够顺利地反编译出.h文件(com_example_testndk_HelloJni.h),那就恭喜你了。下面咱们再接再厉,务必做得更完美些,我们的目标是,要让com_example_testndk_HelloJni.h直接在jni文件夹下生成,因为我们要在jni目录下写本地文件hello_jni.c。jni文件夹在工程根目录下,可以参考本篇开头的那张NDK工程架构图。

   现在用到了javah命令的 -d 选项,如本篇前边部分介绍javah命令时所说,这个选项用来设置 生成的头文件的输出路径。

   考虑到这种需要,反编译命令修改如下:

   

D:\Program Files\Android\workspace\TestNDK>javah -classpath "D:\Program Files\Android\SDK\platforms\android-9\android.jar";bin/classes -d jni com.example.testndk.HelloJni

上面命令的 -d 表示要指定头文件的保存目录,jni 就是目录值,是当前目录(TestNDK工程目录)下的子目录。

 编译的结果如下所示:

  1分钟攻克NDK开发中javah不能反编译的有关问题


结尾:如果你真正理解了上面所讲的javah用法,那么请看下面两个命令,这两个命令和上文距离此处最近的这个命令达到了同样的功能,你能看懂吗?


1.

D:\Program Files\Android\workspace\TestNDK\bin>javah -classpath "D:\Program Files\Android\SDK\platforms\android-9\android.jar";classes -d ../jni com.example.testndk.HelloJni

2.

D:\Program Files\Android\workspace\TestNDK\bin\classes>javah -classpath "D:\Program Files\Android\SDK\platforms\android-9\android.jar";. -d ../../jni com.example.testndk.HelloJni

另外,本篇文章的核心----->头文件"com.example.testndk.HelloJni.h“的内容粘贴如下,供有兴趣者揣摩其中的要旨:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_testndk_HelloJni */

#ifndef _Included_com_example_testndk_HelloJni
#define _Included_com_example_testndk_HelloJni
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_testndk_HelloJni
 * Method:    stringFromJNI
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_testndk_HelloJni_stringFromJNI
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif