不会写JNI?最简单的写法,你知道吗?

Q:读完这边文章之后你能收获什么?
A:不知道你们写过jni吗,你要是一点也没了解过先别看完这段话,先去看下面第一点传统的JNI是怎么写的,然后再回来看剩下的话。

接着:你还在通过类型转换的方式不断去jni.h文件查找类型从而写出对应C或者C++的jni代码吗?其实这不是java,也不是C或者C++,相当于重新学习一门新的语言JNI语言,这样太浪费时间了。通过Swig我们可以解放我们的双手,专心写我们正宗的C或者C++代码,不需要写这种半JNI半C的语言,也不需要我们去写Java层的native方法,是不是很神奇,是不是很想学?别急,通过这篇文章之后你就可以随心所欲专心致志写自己的C或者C++代码了。

生成 jni方式有两种方式。一种是通过SWIG从C++代码生成过度的java代码;另一种是通过javah的方式从java代码自动生成过度的C++代码。两种方式下的步骤流程正好相反。采用第二种方式生成jni,实现JNI封装代码和处理数据类型之间转换繁琐且耗时,因此本文采用swig的方式生成java代码。先介绍下第二种传统方式

一、解析传统的JNI写法

注意:这里不详细介绍JNI的传统写法,因为我的最终目的是不需要写这些文件,但是你还是得去先了解下传统的写法是怎么样的,这样才能对下面我介绍的方法比较深入,这里我介绍一遍文章,写的很详细,你们可以去看看它里面传统JNI写法。但是你只需要看完第八点就行了。后面介绍怎么生成.so文件看我这里介绍比较详细。不怎么熟悉JNI,NDK的也可以先去了解下他的第一篇文章。
https://www.jianshu.com/p/b4431ac22ec2

1.写Java层的本地方法
public class JNI {

    static {
        System.loadLibrary("Hello");
    }

    /**
     * 定义native方法
     * 调用C代码对应的方法
     * @return
     * cjh.com.example.ndk.JNI
     */
    public native String sayHello();
    
}
2.通过javah生成头文件然后写对应的C实现方法
/**
 * jstring :返回值
 * Java_全类名_方法名
 * JNIEnv* env:里面有很多方法
 * jobject jobj:谁调用了这个方法就是谁的实例
 * 当前就是JNI.this
 * cjh.com.example.ndk.JNI.sayHello
 */

 jstring Java_cjh_com_ndkdemo_JNI_sayHello(JNIEnv* env,jobject jobj){

     //jstring     (*NewStringUTF)(JNIEnv*, const char*);
     char* text = "I am from c!!!";
     return  (*env)->NewStringUTF(env,text);
 }
3.生成.so文件

这边先不介绍怎么生成.so文件,后面我会详细介绍。

4.开始使用
 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String result = new JNI().sayHello();
        System.out.println("result==" + result);
    }
5.分析

你是不是对下面这个代码很晕

 jstring Java_cjh_com_ndkdemo_JNI_sayHello(JNIEnv* env,jobject jobj){
     //jstring     (*NewStringUTF)(JNIEnv*, const char*);
     char* text = "I am from c!!!";
     return  (*env)->NewStringUTF(env,text);
 }

这到底是C还是C++还是Java,不,你错了,这是JNI特有的语法,你还得在找string对应的JNI的类型jstring.
JNIEnv到底是什么啊,怎么用啊,我怎么知道它里面的生成字符串的方法是哪个啊,这里面这个多个方法,我怎么知道什么意思啊?你是不是很多疑问。没学过JNI之前听别人说都是写C或者C++的啊,怎么变成写JNI语法了,你骗我,我不学了。

别...放弃,下面跟着我来,你就不需要写这段最复杂最讨厌的代码了,只需要写我们熟悉的C或者C++就行了

二、Swig

1.Swig是什么?

SWIG(Simplified Wrapper and Interface Generator)是一个将C/C++接口转换为其他语言接口的工具,从而可以讲C/C++的库集成到其他语言的系统中。目前SWIG已经可以支持Python, Java, C#,Ruby,PHP,R语言等十多种语言。 是不是还是不清楚,我跟你说啊。来喽,Swig是C++或者C开发人员经常使用的工具,通过它,你就可以生成上面那段烦人的代码了。是不是很神奇,你先听我说它是怎么实现的。你只需要写我们熟悉的C或者C++的.c、.cpp文件就行了。然后你再写一个Swig的配置文件,是.i结尾的。然后Swig命令就可以生成上面那段烦人的代码了,你什么都不需要看,也不需要去了解里面是什么内容。通通抛弃。它还会生成Java的接口文件,什么是Java接口文件,我们之前传统的写法不是需要在Java中先写我们的native方法吗,去供上层调用。这个就是Java成的native文件,我们也不需要写它帮我们生成了,我们只需要把这些文件打包成jar包然后引用就行了。

好累啊,我不想写下去了,我没动力。答应我,给我三连好不好,好不好,好不好。不然我放弃了.....

2.安装使用

下载链接:http://www.swig.org/download.html
下载完成后配置环境变量,这个你就得去百度下很简答的,我这里不说那么多了,好嘞的哦。
官方资料:http://www.swig.org/Doc3.0/SWIGDocumentation.html#Android_examples

3.总体流程

这里先总结下大概流程,在你脑海中有个大致过程,看起来会轻松很多。
1)写C或者C++
2)写Swig的配置文件Unix.i
3)使用Swig命令生成文件
4)编译.so文件和打包jar
大概先这么说,下面看我详细介绍。

c或者c++编译生成.so动态库包含两种方式:一种是通过创建Android.mk文件采用ndk-build编译的方式,这种方式一般只用于老版本的安卓项目中,因此已经不推荐使用。第二种方式就是采用cmake构建工具的方式直接生成.so动态库,这种方式是当前主流方式,因此本文采用cmake方式进行编译生成.so动态库。

四、操作步骤

按照我的操作步骤来,后面你就懂流程了。我这边只是介绍大概流程,参照的还是这边文章,他这里面有图,我这边只是大概说下流程。你先去那边了解下大概流程,什么是mk编译和CMake编译,大概看下就回来,他那边没有介绍Swig,具体还是看我这边。https://www.jianshu.com/p/b4431ac22ec2

1、新建工程

1)新建一个工程

新建Android工程时勾选Include C++ support,之后按照默认下一步。Customize C++ SupportCustom的自定义项目中包含三部分。以下说明:
· C++ Standard:即C++标准,使用下拉列表选择你希望使用的C++的标准,选择Toolchain Default 会使用默认的CMake设置。
· Exceptions Support:如果你希望启用对C++异常处理的支持,请选择此复选框。如果启动此复选框,Android Studio 会将-fexceptions标志添加到模块级build.gradle文件的cppFlags中,Gradle会将其传递到CMake。
· Runtime Type Information Support:如果开发者希望支持RTTI,请选中此复选框。如果启用此复选框,Android Studio 会将-frtti标志添加到模块级build.gradle文件的cppFlags中,Gradle会将其传递到CMake。

2)新建项目文件结构说明

· 在 cpp 文件夹中:可以找到属于项目的所有原生源文件等构建库。对于新项目,Android Studio会创建一个示例C++源文件 native-lib.cpp,并将其置于应用模块src/main/cpp/目录中。这个示例代码提供了一个简单的C++函数stringFromJNI(),此函数可以返回字符串“Hello from C++”
· 在 External Build Files 文件夹中:可以找到CMake或 ndk-build 的构建脚本。与build.gradle文件指示Gradle构建应用一样,CMake和ndk-build需要一个构建脚本来了解如何构原生库。对于新项目,Android Studio 会创建一个CMake 构建脚本CMakeLists.txt,并将其置于模块根目录中。

2.编写C++源代码

1)新建文件夹

在src/main目录下面创建jni文件夹,与java同级。jni目录下面创建src文件夹用来保存源代码,也就是.cpp和.h等源代码。这里采用C++方式进行演示。

Hello.h

#include <cstring>
using namespace std;
#ifndef NDKDEMO_HELLO_H
#define NDKDEMO_HELLO_H

string getText();

#endif //NDKDEMO_HELLO_H

Hello.cpp

#include "Hello.h"
string getText(){
    return "I am from c++";
}

2.编写Unix.i文件

在jni目录下面创建一个.i文件结尾的swig解析文件,本demo中创建为Unix.i文件。文件如下:

--Unix.i文件

%module(directors="1") HelloLib   //指定模块名 directors="1" 代表可以对C++的类在JAVA中继承
%include "std_string.i"
%{
#include "Hello.cpp"//这是最终打包成Unix_wrap.cxx文件里面包含的C或者C++内容
%}
%include "Hello.h"   //这是生成的Java包含的内容
3、Swig生成文件

1)执行命令

在终端切换当前路径到jni目录下面,开始使用swig命令编译生成java代码。命令如下:

swig.exe -c++ -java -package com.geo.earthworklib -outdir F:/AllProjects/EarthworkLib/app/src/main/java/com/geo/earthworklib -o Unix_wrap.cxx Unix.i

//-c++ 指定当前语言是C++还是C,默认是C,只有这两种,没有其他的
//-java 生成的包装语言,可以使其他任何一种支持的语言 如-python -csharp
//-package 生成的swig java类的包名
//-outdir java文件放在哪里
//-o 输出的CXX文件的文件名
//i文件路径

2)生成的文件说明

此时就在当前目录会生成一个Unix_wrap.cxx文件,这个就是生成的jni语法的c++包装类,也就是使用cmake编译生成.so的源文件。此时还会在设置的-outdir路径下面生成java接口文件,这个接口文件也就是最终打包成jar包调用的。

四、CMake生成.so文件

1.编写CMakeLists.txt
#指定CMake的最小版本
cmake_minimum_required(VERSION 3.4.1)

#设置生成的so动态库最后输出的路径
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI})
#创建一个静态或者动态库,并提供其关联的源文件路径,开发者可以定义多个库,
#CMake会自动去构建它们。Gradle可以自动将它们打包进APK中。
#第一个参数——native-lib:是库的名称
#第二个参数——SHARED:是库的类别,是动态的还是静态的
#第三个参数——src/main/cpp/native-lib.cpp:是库的源文件的路径
add_library( # Sets the name of the library.
             earthworklib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/jni/Unix_wrap.cxx )

2.检查当前module的build.gradle文件下检查配置是否正确
externalNativeBuild {
    cmake {
    //创建项目时添加额配置
        cppFlags "-frtti -fexceptions"
    //指定生成的cpu架构
        abiFilters 'armeabi-v7a','x86'
    }
}
externalNativeBuild {
    cmake {
    //CMakeLists.txt文件的路径
        path "CMakeLists.txt"
    }
}
3.编译

编译完成之后会在CMakeLists.txt文件中指定生成.so的文件目录下这里也就是libs文件夹下生成.so文件

4.生成Jar包

给链接给你们学习,自己去学习下.
注意下现在生成的原始jar包位置在这里app\build\intermediates\packaged-classes
https://www.jianshu.com/p/1a69e2fcaed5

五、总结

其实说白了就是你先编写好.cpp源文件,然后使用Swig工具就可以生成含jni语法的Unix.wrap.cxx文件,这个文件然后通过传统的方式mk或者cmake方式最终就可以生成.so库了。Swig也会生成Java接口文件,只需要把这个文件打包成Jar包,这样.so和jar包都有了就OK了。其实就分两步,第一步是使用Swig生成文件,第二部就是使用Cmake或者mk生成.so。大功告成,就是这样。你只需要去编写Swig的.i文件后面就是一系列自然的事了。

关于Swig的参考资料少之又少,下面给链接给你们参考学习。
https://www.jianshu.com/p/a91f4e3e20c3

你是不是觉得还要去了解Swig命令,还要去了解CMake是什么,不知道CMakeLists文件怎么写。这么多步骤好烦杂啊,下面一篇文章我会介绍更简单的方法,什么都不需要干,只需要编译一下什么都有了。你是不是觉得我在吹牛,你过来看啊,你要是累了你先大概在回忆一下大概流程,后面我会把Swig包含在我写的CMakeLists文件里面,通过构建工具一步解决。

六、最后

我其实有很多话想说明的,但是写起来实在是太耗费时间了,很多还是没怎么解释清楚的,我知道你们肯定还有很多困惑,你需要多看两遍,熟悉操作之后就很简单了。你们不懂得可以下面留言评论,我知道的话一定知无不言言无不尽。

最后,创作不易,感谢您的阅读,要是有收获请记得三连点击,别告诉我下次一定!
您的支持是我写作的最大动力!谢谢亲

image.png

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,265评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,274评论 1 288
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,087评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,479评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,782评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,218评论 1 207
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,594评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,316评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,955评论 1 237
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,274评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,803评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,177评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,732评论 3 229
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,953评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,687评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,263评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,189评论 2 258