问题

最近在用kotlin写jni,但是生成头文件的时候遇到了些问题。

首先 javah 在java >= 1.9 就被取消用javac -h代替,但是javac对kotlin不适用,kotlinc也没有 -h 这个生成头文件的选项。

解决方法

在stackoverflow论坛找到了个解决方案,那个人提供了一个gradle task,大概原理是先用complieKotlin任务(或kotlinc)生成class字节码,再用javac编译回java文件,然后再调用javac -h 针对那个java文件生成jni头文件,我稍微修复了下,然后迁移到gradle kotlin dsl(就kts脚本),代码在下面。

使用方法

复制到build.gradle.kts的最外层就可以了,然后sync一下gradle,然后在gradle task里的build分类下就有generate jniheader这个任务了,StackOverflow原贴地址https://stackoverflow.com/a/65661275/14646226

需要改下代码里的inputs.dir("src/main/kotlin")到你的kt源码文件夹, outputs.dir("src/main/generated/jni")到你想的输出文件夹

代码

val generateJniHeaders: Task by tasks.creating {
group = "build"
dependsOn(tasks.getByName("compileKotlin")) // For caching
inputs.dir("src/main/kotlin")
outputs.dir("src/main/generated/jni") doLast {
val javaHome = org.gradle.internal.jvm.Jvm.current().javaHome
val javap = javaHome.resolve("bin").walk().firstOrNull { it.name.startsWith("javap") }?.absolutePath ?: error("javap not found")
val javac = javaHome.resolve("bin").walk().firstOrNull { it.name.startsWith("javac") }?.absolutePath ?: error("javac not found")
val buildDir = file("build/classes/kotlin/main")
val tmpDir = file("build/tmp/jvmJni").apply { mkdirs() } val bodyExtractingRegex = """^.+\Rpublic \w* ?class ([^\s]+).*\{\R((?s:.+))\}\R$""".toRegex()
val nativeMethodExtractingRegex = """.*\bnative\b.*""".toRegex() buildDir.walkTopDown()
.filter { "META" !in it.absolutePath }
.forEach { file ->
if (!file.isFile) return@forEach val output = com.gradle.publish.plugin.dep.org.apache.commons.io.output.ByteArrayOutputStream().use {
project.exec {
commandLine(javap, "-private", "-cp", buildDir.absolutePath, file.absolutePath)
standardOutput = it
}.assertNormalExitValue()
it.toString()
} val (qualifiedName, methodInfo) = bodyExtractingRegex.find(output)?.destructured ?: return@forEach val lastDot = qualifiedName.lastIndexOf('.')
val packageName = qualifiedName.substring(0, lastDot)
val className = qualifiedName.substring(lastDot+1, qualifiedName.length) val nativeMethods =
nativeMethodExtractingRegex.findAll(methodInfo).mapNotNull { it.groups }.flatMap { it.asSequence().mapNotNull { group -> group?.value } }.toList()
if (nativeMethods.isEmpty()) return@forEach val source = buildString {
appendLine("package $packageName;")
appendLine("public class $className {")
for (method in nativeMethods) {
if ("()" in method) appendLine(method)
else {
val updatedMethod = StringBuilder(method).apply {
var count = 0
var i = 0
while (i < length) {
if (this[i] == ',' || this[i] == ')') insert(i, " arg${count++}".also { i += it.length + 1 })
else i++
}
}
appendLine(updatedMethod)
}
}
appendLine("}")
}
val outputFile = tmpDir.resolve(packageName.replace(".", "/")).apply { mkdirs() }.resolve("$className.java").apply { delete() }.apply { createNewFile() }
outputFile.writeText(source) project.exec {
commandLine(javac, "-h", "src/main/generated/jni", outputFile.absolutePath)
}.assertNormalExitValue()
}
}
}

gradle task位置截图

kotlin gradle 生成jni头文件的更多相关文章

  1. ubuntu下整合eclipse和javah生成jni头文件开发android的native程序(转)

    本文介绍两种利用javah命令生成jni头文件的方法,第一种为大众所知的javah命令,第二种为整合javah到eclipse里面.推荐第二种方式,方便快捷,随时修改随时生成 0:前提和条件: 1:u ...

  2. ubuntu下整合eclipse和javah生成jni头文件开发android的native程序

    0:前言: 这两天一直在研究用android的jni调用第三方库,上网搜方法,但是都是泛泛而谈,没有demo,经过我几番折磨,写了n多的helloword工程,总是不成功,工程名字也就由helloow ...

  3. Android NDK入门实例 计算斐波那契数列一生成jni头文件

    最近要用到Android NDK,调用本地代码.就学了下Android NDK,顺便与大家分享.下面以一个具体的实例计算斐波那契数列,说明如何利用Android NDK,调用本地代码.以及比较本地代码 ...

  4. Java 生成 JNI 头文件

    [参考文章]:javah javac 命令行生成native的方法教程 [参考文章]:用javah 导出类的头文件, 常见的错误及正确的使用方法 项目需要调用 C++动态库,由于项目包结构发生变化,需 ...

  5. javah生成jni头文件时报错 Error: cannot access android.support...

    javah生成jni头文件时报错: Error: cannot access android.support.v7.app.AppCompatActivity class file for andro ...

  6. [转]ubuntu下整合eclipse和javah生成jni头文件开发android的native程序

    转载自:http://blog.csdn.net/jiuyueguang/article/details/9404237 本文介绍两种利用javah命令生成jni头文件的方法,第一种为大众所知的jav ...

  7. 一键生成JNI头文件方法二

    经常使用java的同学一定都接触过JNI(Java Native Interface)吧.JNI为我们提供了java<---->C/C++之间的接口,使得我们可以在java中调用C程序,以 ...

  8. 使用javah生成jni 头文件和使用ndk编译so库

    1.jni 首先clean Project,在makeProject生成对应的class文件 然后点出命名框,输入命令: cd app/build/intermediates/classes/debu ...

  9. Android NDK学习(3)使用Javah命令生成JNI头文件 .

    转:http://www.cnblogs.com/fww330666557/archive/2012/12/14/2817387.html 第一步: 在Eclipse中创建android项目,并声明N ...

随机推荐

  1. 如何在我的EC2实例状态更改时获取自定义电子邮件通知

    具体详情,请参见: https://amazonaws-china.com/cn/premiumsupport/knowledge-center/ec2-email-instance-state-ch ...

  2. Linux_yum工具基本概述

    一.什么是yum 1️⃣:yum是yellowdog update manager的简称,它能够实现rpm管理的所有操作,并能够自动解决各rpm包之间的依赖关系. 2️⃣:yum是rpm的前端工具,是 ...

  3. vim 编辑器高级用法

    vim编辑器介绍 如果没有安装vim使用下面方式安装 yum -y install vim vi与vim的不同 两者最大的不同:vim可以高亮显示,vi不可以. vim三种工作模式 普通模式 # 光标 ...

  4. REST 架构风格详解

    什么是 REST 架构风格 REST(Representational State Transfer)是表述性状态转移.分布式超媒体软件的一种架构风格,它基于使用 HTTP.URI 等现有的广泛流行的 ...

  5. 设置添加SSH-(转自破男孩)

    很多朋友在用github管理项目的时候,都是直接使用https url克隆到本地,当然也有有些人使用 SSH url 克隆到本地.然而,为什么绝大多数人会使用https url克隆呢? 这是因为,使用 ...

  6. 6.1 tar:打包备份

    tar命令     在Linux系统里,tar是将多个文件打包在一起,并且可以实现解压打包的文件的命令.是系统管理员最常用的命令之一,tar命令不但可以实现对多个文件进行打包,还可以对多个文件打包后进 ...

  7. Centos下删除文件后空间并未释放

    [root@DeviceSP /]# df -h Filesystem Size Used Avail Use% Mounted on /dev/vda3 90G 82G 3.8G 96% / tmp ...

  8. 大作!webpack详细配置

    webpack学习之旅 好好学习 天天向上!遇到bug,不要慌! 文章目录 webpack学习之旅 大一统的模块化规范--ES6模块化 1.node.js中通过babel体验ES6模块化 2.ES6模 ...

  9. GO学习-(29) Go语言操作etcd

    Go语言操作etcd etcd是近几年比较火热的一个开源的.分布式的键值对数据存储系统,提供共享配置.服务的注册和发现,本文主要介绍etcd的安装和使用. etcd etcd介绍 etcd是使用Go语 ...

  10. K8s - Kubernetes重要概念介绍(Cluster、Master、Node、Pod、Controller、Service、Namespace)

    K8s - Kubernetes重要概念介绍(Cluster.Master.Node.Pod.Controller.Service.Namespace)       Kubernetes 是目前发展最 ...