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文件里面,通过构建工具一步解决。
六、最后
我其实有很多话想说明的,但是写起来实在是太耗费时间了,很多还是没怎么解释清楚的,我知道你们肯定还有很多困惑,你需要多看两遍,熟悉操作之后就很简单了。你们不懂得可以下面留言评论,我知道的话一定知无不言言无不尽。
最后,创作不易,感谢您的阅读,要是有收获请记得三连点击,别告诉我下次一定!
您的支持是我写作的最大动力!谢谢亲
网友评论