Q:讀完這邊文章之后你能收獲什么?
A:不知道你們寫(xiě)過(guò)jni嗎,你要是一點(diǎn)也沒(méi)了解過(guò)先別看完這段話(huà),先去看下面第一點(diǎn)傳統(tǒng)的JNI是怎么寫(xiě)的,然后再回來(lái)看剩下的話(huà)。
接著:你還在通過(guò)類(lèi)型轉(zhuǎn)換的方式不斷去jni.h文件查找類(lèi)型從而寫(xiě)出對(duì)應(yīng)C或者C++的jni代碼嗎?其實(shí)這不是java,也不是C或者C++,相當(dāng)于重新學(xué)習(xí)一門(mén)新的語(yǔ)言JNI語(yǔ)言,這樣太浪費(fèi)時(shí)間了。通過(guò)Swig我們可以解放我們的雙手,專(zhuān)心寫(xiě)我們正宗的C或者C++代碼,不需要寫(xiě)這種半JNI半C的語(yǔ)言,也不需要我們?nèi)?xiě)Java層的native方法,是不是很神奇,是不是很想學(xué)?別急,通過(guò)這篇文章之后你就可以隨心所欲專(zhuān)心致志寫(xiě)自己的C或者C++代碼了。
生成 jni方式有兩種方式。一種是通過(guò)SWIG從C++代碼生成過(guò)度的java代碼;另一種是通過(guò)javah的方式從java代碼自動(dòng)生成過(guò)度的C++代碼。兩種方式下的步驟流程正好相反。采用第二種方式生成jni,實(shí)現(xiàn)JNI封裝代碼和處理數(shù)據(jù)類(lèi)型之間轉(zhuǎn)換繁瑣且耗時(shí),因此本文采用swig的方式生成java代碼。先介紹下第二種傳統(tǒng)方式
一、解析傳統(tǒng)的JNI寫(xiě)法
注意:這里不詳細(xì)介紹JNI的傳統(tǒng)寫(xiě)法,因?yàn)槲业淖罱K目的是不需要寫(xiě)這些文件,但是你還是得去先了解下傳統(tǒng)的寫(xiě)法是怎么樣的,這樣才能對(duì)下面我介紹的方法比較深入,這里我介紹一遍文章,寫(xiě)的很詳細(xì),你們可以去看看它里面?zhèn)鹘y(tǒng)JNI寫(xiě)法。但是你只需要看完第八點(diǎn)就行了。后面介紹怎么生成.so文件看我這里介紹比較詳細(xì)。不怎么熟悉JNI,NDK的也可以先去了解下他的第一篇文章。
http://www.itdecent.cn/p/b4431ac22ec2
1.寫(xiě)Java層的本地方法
public class JNI {
static {
System.loadLibrary("Hello");
}
/**
* 定義native方法
* 調(diào)用C代碼對(duì)應(yīng)的方法
* @return
* cjh.com.example.ndk.JNI
*/
public native String sayHello();
}
2.通過(guò)javah生成頭文件然后寫(xiě)對(duì)應(yīng)的C實(shí)現(xiàn)方法
/**
* jstring :返回值
* Java_全類(lèi)名_方法名
* JNIEnv* env:里面有很多方法
* jobject jobj:誰(shuí)調(diào)用了這個(gè)方法就是誰(shuí)的實(shí)例
* 當(dāng)前就是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文件,后面我會(huì)詳細(xì)介紹。
4.開(kāi)始使用
@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.分析
你是不是對(duì)下面這個(gè)代碼很暈
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,不,你錯(cuò)了,這是JNI特有的語(yǔ)法,你還得在找string對(duì)應(yīng)的JNI的類(lèi)型jstring.
JNIEnv到底是什么啊,怎么用啊,我怎么知道它里面的生成字符串的方法是哪個(gè)啊,這里面這個(gè)多個(gè)方法,我怎么知道什么意思???你是不是很多疑問(wèn)。沒(méi)學(xué)過(guò)JNI之前聽(tīng)別人說(shuō)都是寫(xiě)C或者C++的啊,怎么變成寫(xiě)JNI語(yǔ)法了,你騙我,我不學(xué)了。
別...放棄,下面跟著我來(lái),你就不需要寫(xiě)這段最復(fù)雜最討厭的代碼了,只需要寫(xiě)我們熟悉的C或者C++就行了
二、Swig
1.Swig是什么?
SWIG(Simplified Wrapper and Interface Generator)是一個(gè)將C/C++接口轉(zhuǎn)換為其他語(yǔ)言接口的工具,從而可以講C/C++的庫(kù)集成到其他語(yǔ)言的系統(tǒng)中。目前SWIG已經(jīng)可以支持Python, Java, C#,Ruby,PHP,R語(yǔ)言等十多種語(yǔ)言。 是不是還是不清楚,我跟你說(shuō)啊。來(lái)嘍,Swig是C++或者C開(kāi)發(fā)人員經(jīng)常使用的工具,通過(guò)它,你就可以生成上面那段煩人的代碼了。是不是很神奇,你先聽(tīng)我說(shuō)它是怎么實(shí)現(xiàn)的。你只需要寫(xiě)我們熟悉的C或者C++的.c、.cpp文件就行了。然后你再寫(xiě)一個(gè)Swig的配置文件,是.i結(jié)尾的。然后Swig命令就可以生成上面那段煩人的代碼了,你什么都不需要看,也不需要去了解里面是什么內(nèi)容。通通拋棄。它還會(huì)生成Java的接口文件,什么是Java接口文件,我們之前傳統(tǒng)的寫(xiě)法不是需要在Java中先寫(xiě)我們的native方法嗎,去供上層調(diào)用。這個(gè)就是Java成的native文件,我們也不需要寫(xiě)它幫我們生成了,我們只需要把這些文件打包成jar包然后引用就行了。
好累啊,我不想寫(xiě)下去了,我沒(méi)動(dòng)力。答應(yīng)我,給我三連好不好,好不好,好不好。不然我放棄了.....
2.安裝使用
下載鏈接:http://www.swig.org/download.html
下載完成后配置環(huán)境變量,這個(gè)你就得去百度下很簡(jiǎn)答的,我這里不說(shuō)那么多了,好嘞的哦。
官方資料:http://www.swig.org/Doc3.0/SWIGDocumentation.html#Android_examples
3.總體流程
這里先總結(jié)下大概流程,在你腦海中有個(gè)大致過(guò)程,看起來(lái)會(huì)輕松很多。
1)寫(xiě)C或者C++
2)寫(xiě)Swig的配置文件Unix.i
3)使用Swig命令生成文件
4)編譯.so文件和打包jar
大概先這么說(shuō),下面看我詳細(xì)介紹。
c或者c++編譯生成.so動(dòng)態(tài)庫(kù)包含兩種方式:一種是通過(guò)創(chuàng)建Android.mk文件采用ndk-build編譯的方式,這種方式一般只用于老版本的安卓項(xiàng)目中,因此已經(jīng)不推薦使用。第二種方式就是采用cmake構(gòu)建工具的方式直接生成.so動(dòng)態(tài)庫(kù),這種方式是當(dāng)前主流方式,因此本文采用cmake方式進(jìn)行編譯生成.so動(dòng)態(tài)庫(kù)。
四、操作步驟
按照我的操作步驟來(lái),后面你就懂流程了。我這邊只是介紹大概流程,參照的還是這邊文章,他這里面有圖,我這邊只是大概說(shuō)下流程。你先去那邊了解下大概流程,什么是mk編譯和CMake編譯,大概看下就回來(lái),他那邊沒(méi)有介紹Swig,具體還是看我這邊。http://www.itdecent.cn/p/b4431ac22ec2
1、新建工程
1)新建一個(gè)工程
新建Android工程時(shí)勾選Include C++ support,之后按照默認(rèn)下一步。Customize C++ SupportCustom的自定義項(xiàng)目中包含三部分。以下說(shuō)明:
· C++ Standard:即C++標(biāo)準(zhǔn),使用下拉列表選擇你希望使用的C++的標(biāo)準(zhǔn),選擇Toolchain Default 會(huì)使用默認(rèn)的CMake設(shè)置。
· Exceptions Support:如果你希望啟用對(duì)C++異常處理的支持,請(qǐng)選擇此復(fù)選框。如果啟動(dòng)此復(fù)選框,Android Studio 會(huì)將-fexceptions標(biāo)志添加到模塊級(jí)build.gradle文件的cppFlags中,Gradle會(huì)將其傳遞到CMake。
· Runtime Type Information Support:如果開(kāi)發(fā)者希望支持RTTI,請(qǐng)選中此復(fù)選框。如果啟用此復(fù)選框,Android Studio 會(huì)將-frtti標(biāo)志添加到模塊級(jí)build.gradle文件的cppFlags中,Gradle會(huì)將其傳遞到CMake。
2)新建項(xiàng)目文件結(jié)構(gòu)說(shuō)明
· 在 cpp 文件夾中:可以找到屬于項(xiàng)目的所有原生源文件等構(gòu)建庫(kù)。對(duì)于新項(xiàng)目,Android Studio會(huì)創(chuàng)建一個(gè)示例C++源文件 native-lib.cpp,并將其置于應(yīng)用模塊src/main/cpp/目錄中。這個(gè)示例代碼提供了一個(gè)簡(jiǎn)單的C++函數(shù)stringFromJNI(),此函數(shù)可以返回字符串“Hello from C++”
· 在 External Build Files 文件夾中:可以找到CMake或 ndk-build 的構(gòu)建腳本。與build.gradle文件指示Gradle構(gòu)建應(yīng)用一樣,CMake和ndk-build需要一個(gè)構(gòu)建腳本來(lái)了解如何構(gòu)原生庫(kù)。對(duì)于新項(xiàng)目,Android Studio 會(huì)創(chuàng)建一個(gè)CMake 構(gòu)建腳本CMakeLists.txt,并將其置于模塊根目錄中。
2.編寫(xiě)C++源代碼
1)新建文件夾
在src/main目錄下面創(chuàng)建jni文件夾,與java同級(jí)。jni目錄下面創(chuàng)建src文件夾用來(lái)保存源代碼,也就是.cpp和.h等源代碼。這里采用C++方式進(jìn)行演示。
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.編寫(xiě)Unix.i文件
在jni目錄下面創(chuàng)建一個(gè).i文件結(jié)尾的swig解析文件,本demo中創(chuàng)建為Unix.i文件。文件如下:
--Unix.i文件
%module(directors="1") HelloLib //指定模塊名 directors="1" 代表可以對(duì)C++的類(lèi)在JAVA中繼承
%include "std_string.i"
%{
#include "Hello.cpp"http://這是最終打包成Unix_wrap.cxx文件里面包含的C或者C++內(nèi)容
%}
%include "Hello.h" //這是生成的Java包含的內(nèi)容
3、Swig生成文件
1)執(zhí)行命令
在終端切換當(dāng)前路徑到j(luò)ni目錄下面,開(kāi)始使用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++ 指定當(dāng)前語(yǔ)言是C++還是C,默認(rèn)是C,只有這兩種,沒(méi)有其他的
//-java 生成的包裝語(yǔ)言,可以使其他任何一種支持的語(yǔ)言 如-python -csharp
//-package 生成的swig java類(lèi)的包名
//-outdir java文件放在哪里
//-o 輸出的CXX文件的文件名
//i文件路徑
2)生成的文件說(shuō)明
此時(shí)就在當(dāng)前目錄會(huì)生成一個(gè)Unix_wrap.cxx文件,這個(gè)就是生成的jni語(yǔ)法的c++包裝類(lèi),也就是使用cmake編譯生成.so的源文件。此時(shí)還會(huì)在設(shè)置的-outdir路徑下面生成java接口文件,這個(gè)接口文件也就是最終打包成jar包調(diào)用的。
四、CMake生成.so文件
1.編寫(xiě)CMakeLists.txt
#指定CMake的最小版本
cmake_minimum_required(VERSION 3.4.1)
#設(shè)置生成的so動(dòng)態(tài)庫(kù)最后輸出的路徑
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI})
#創(chuàng)建一個(gè)靜態(tài)或者動(dòng)態(tài)庫(kù),并提供其關(guān)聯(lián)的源文件路徑,開(kāi)發(fā)者可以定義多個(gè)庫(kù),
#CMake會(huì)自動(dòng)去構(gòu)建它們。Gradle可以自動(dòng)將它們打包進(jìn)APK中。
#第一個(gè)參數(shù)——native-lib:是庫(kù)的名稱(chēng)
#第二個(gè)參數(shù)——SHARED:是庫(kù)的類(lèi)別,是動(dòng)態(tài)的還是靜態(tài)的
#第三個(gè)參數(shù)——src/main/cpp/native-lib.cpp:是庫(kù)的源文件的路徑
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.檢查當(dāng)前module的build.gradle文件下檢查配置是否正確
externalNativeBuild {
cmake {
//創(chuàng)建項(xiàng)目時(shí)添加額配置
cppFlags "-frtti -fexceptions"
//指定生成的cpu架構(gòu)
abiFilters 'armeabi-v7a','x86'
}
}
externalNativeBuild {
cmake {
//CMakeLists.txt文件的路徑
path "CMakeLists.txt"
}
}
3.編譯
編譯完成之后會(huì)在CMakeLists.txt文件中指定生成.so的文件目錄下這里也就是libs文件夾下生成.so文件
4.生成Jar包
給鏈接給你們學(xué)習(xí),自己去學(xué)習(xí)下.
注意下現(xiàn)在生成的原始jar包位置在這里app\build\intermediates\packaged-classes
http://www.itdecent.cn/p/1a69e2fcaed5
五、總結(jié)
其實(shí)說(shuō)白了就是你先編寫(xiě)好.cpp源文件,然后使用Swig工具就可以生成含jni語(yǔ)法的Unix.wrap.cxx文件,這個(gè)文件然后通過(guò)傳統(tǒng)的方式mk或者cmake方式最終就可以生成.so庫(kù)了。Swig也會(huì)生成Java接口文件,只需要把這個(gè)文件打包成Jar包,這樣.so和jar包都有了就OK了。其實(shí)就分兩步,第一步是使用Swig生成文件,第二部就是使用Cmake或者mk生成.so。大功告成,就是這樣。你只需要去編寫(xiě)Swig的.i文件后面就是一系列自然的事了。
關(guān)于Swig的參考資料少之又少,下面給鏈接給你們參考學(xué)習(xí)。
http://www.itdecent.cn/p/a91f4e3e20c3
你是不是覺(jué)得還要去了解Swig命令,還要去了解CMake是什么,不知道CMakeLists文件怎么寫(xiě)。這么多步驟好煩雜啊,下面一篇文章我會(huì)介紹更簡(jiǎn)單的方法,什么都不需要干,只需要編譯一下什么都有了。你是不是覺(jué)得我在吹牛,你過(guò)來(lái)看啊,你要是累了你先大概在回憶一下大概流程,后面我會(huì)把Swig包含在我寫(xiě)的CMakeLists文件里面,通過(guò)構(gòu)建工具一步解決。
六、最后
我其實(shí)有很多話(huà)想說(shuō)明的,但是寫(xiě)起來(lái)實(shí)在是太耗費(fèi)時(shí)間了,很多還是沒(méi)怎么解釋清楚的,我知道你們肯定還有很多困惑,你需要多看兩遍,熟悉操作之后就很簡(jiǎn)單了。你們不懂得可以下面留言評(píng)論,我知道的話(huà)一定知無(wú)不言言無(wú)不盡。
最后,創(chuàng)作不易,感謝您的閱讀,要是有收獲請(qǐng)記得三連點(diǎn)擊,別告訴我下次一定!
您的支持是我寫(xiě)作的最大動(dòng)力!謝謝親
