Java 知道这些东西 不会写_不会写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.安装使用

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

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

六、最后

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

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

您的支持是我写作的最大动力!谢谢亲

0d2d29fa9f72

image.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值