自动生成jni方法的解决方案

本文介绍了JNI通信规范及手工处理的弊端,引出google开发的jni_generator.py脚本。它是功能增强的javah,能识别特定函数并生成对应jni方法。还讲述了从webrtc源码获取该脚本的步骤、常用参数、不同输入类型的使用示例,以及在脚本中批量生成jni方法的注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

JNI定义了Java和C/C++之间的通信规范,当中存在不少机械式定义,像C/C++中原生函数名的一部分是Java中package名称。一旦相互调用的函数多了,依旧用手工去处理,不但浪费时间还容易出错,于是java提供一个叫javah的实用程序帮助开发者把*.java声明的JNI方法转化成C/C++头文件*.h。

javah只是实现了机械式定义中部分提取,一些厂家不满足那点处理,甚至要加上非JNI规范的私有处理。举个例子,让Java代码出现@JNINamespace关键字,表示接下调用的C/C++原生函数是在这个命名空间中实现。@CalledByNative则用于加在函数前,表示这Java函数将被C/C++调用。——为实现这些目标,google开发了一个用python语言写的脚本包,jni_generator.py是这个脚本包中用户要调用的唯一命令。简单来说,jni_generator.py是一个跨平台的、功能增强的javah,它能够识别java文件中被@CalledByNative修饰、以及被native关键字修饰且函数名以native开头的函数,并生成对应的jni方法以及简单的函数声明(具体业务需要自己去实现)。

 

获取jni_generator.py

这里讲述如何从webrtc源码得到脚本包。jni_generator脚本本身是在webrtc的源码目录里,当拉取到webrtc的源码后,本身这个脚本就可以运行了(但是从谷歌官方上下载下来的源码没有找到这个文件,所以还是建议用前边我贴的超链接)。但webrtc源码及依赖库都很大,要想分离使用这个脚本,最简单的就是只取出这个脚本用到的python依赖,这样就可以使用这个脚本独立运行了。

提取的步骤很简单。这里假定webrtc源码目录为src。

  1. 提取src/base/android/jni_generator/这个目录里的所有文件,并保留目录层次。这是jni_generator.py所在的目录。
  2. 提取上面脚本依赖的库,提取src/build/目录里的所有文件及目录,这里全部提取只是为了方便快速,其实很多文件是不必要的。
  3. 继续提取依赖,提取src/buildtools/目录的所有文件及目录,理由同2.
  4. 最后要提取的是src/third_party/catapult目录

到此为止,一个可以独立使用的jni_generator.py就完成了。

 

使用jni_generator.py

以下只列出jni_generator.py常用的四个参数。

  • --input_file。指示要解析的单个*.java或*.class,生成的*.h放在“--output_dir”指定的目录。是*.class时,jni_generator.py会先调用javap反編译出*.java。
  • --output_dir。指示生成的*.h要存放到的目录。
  • --jar_file。此次要解析一个*.jar文件,这时“--input_file”表示的是该jar中某个预編译出的.class。
  • --javap(默认值:javap)。javap在硬盘上的完整路径,包括文件名javap.exe。

根据不同类型的输入文件,使用jni_generator.py可分为两类,一是从*.java输入,二是从*.class输入。

jni_generator.py --input_file MainActivity.java --output_dir ./jni

示例输入是*.java。MainActivity.java已是源码文件,jni_generator分析文件中文本就能生成MainActivity_jni.h,然后把它放在“./jni”目录。

jni_generator.py --input_file $mnt/java/lang/Integer.class --output_dir generated_external_classes_jni/jni
jni_generator.py --jar_file rt.jar --input_file java/lang/Integer.class --output_dir generated_external_classes_jni/jni

示例中两条语句都可认为是从*.class输入,并且实现同样功能。Integer.class是jre/jdk自提供的一个class,它和基它类型class一块被放入rt.jar。

第一条语句中,硬盘真实存在着--input_file表示的Integer.class,像把rt.jar解压到一个目录,然后让指向那里的Integer.class。*.class是从*.java編译生成,jni_generator.py需要把它反編译成*.java,再生成*.h。反編译用的是javap,所以必须要让jni_generator能知道javap在哪里。一种方法是把javap所在路径加入PATH环境变量,第二种则是用--javap专门指示javap在哪里。

第二条语句中,Integer.class放在了*.jar。--jar_file指向这个jar,--input_file则是Integer.class在jar中路径。相比第一条语句,这种情况多了第一个步骤,解压rt.jar,一旦jre/jdk安装有异常,极可能导致解压失败,为此要能成功的条件会比直接在--input_file输入.class时更严格。

 

如何在脚本中批量生成jni方法

以下脚本是我在AS工程中使用的脚本

#!/bin/zsh
#参数1是期望的结尾,参数2是文件夹
function endWith(){
  for i in $2;
  do
    if [[ $i == *$1 ]]
      then
        return 1
    fi
  done
  return 0
}

function read_dir(){
    for file in `ls -a $1`
    do
        if [ -d $1"/"$file ]
        then
            if [[ $file != '.' && $file != '..' ]]
            then
                read_dir $1"/"$file
            fi
        else
            child=$1"/"$file
            endWith .java $child
            if [[ $? == 1 ]]
              then
                echo generate $child"\`s" _jni.h
                #使用时,需要把../../.././jni_generator.py更换为你自己的路径
                python ../../.././jni_generator.py --input_file $child --output_dir ./app/src/main/cpp
            fi
        fi
    done
}

#java文件所在的路径
jniPath="./app/src/main/java/io/agora/jnigeneratortest/"
read_dir $jniPath

下边贴一下成功生成的图片

         注意:对于不包含native方法的java文件,jni_generator.py会报错,不过无伤大雅,不会影响其他java文件。

我的AS工程下载地址:https://download.youkuaiyun.com/download/u013908616/13970014

请大佬们多多指教,多谢。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值