之前寫過一篇比較水的文章Android手機控制電腦擼出HelloWorld
裏面用到了JNI/NDK技術。
這篇文章給大家介紹下JNI/NDK開發。採用的是Android Studio2.2開發環境,使用CMake方式進行開發。
JNI(Java Native Interface)是java與C/C++進行通信的一種技術,使用JNI技術,可以java調用C/C++的函數對象等等,Android中的Framework層與Native層就是採用的JNI技術。
我們知道,Android系統是基於linux開發,採用的是linux內核 ,Android APP開發大部分也要和系統打交道,只是Android FrameWork 幫我們處理了和系統相關的操作, 我們從Android 系統的分成結構可以看出,Android FrameWork是通過JNI與底層的C/C++庫交互,例如:FreeType,OpenGL,SQLite,音視頻等等。
如果我們程序也需要調用自己的C/C++函數庫,就必須用到JNI/NDK開發。
NDK配置(最新的CMake方式)
Android Studio2.2版本已經完全支持ndk開發了。而且默認採用CMake方式。(傳統方式不過多介紹了)
CMake的優勢
- 可以直接的在C/C++代碼中加入斷點,進行調試
- java引用的C/C++中的方法,可以直接ctrl+左鍵進入
- 對於include的頭文件,或者庫,也可以直接的進入
- 不需要配置命令行操作,手動的生成頭文件,不需要配置
android.useDeprecatedNdk=true
屬性
下載
首先需要下載NDK,來到設置界面點擊下載NDK
安裝完NDK,還可以選擇配置一些工具。
- CMake: 外部構建工具。如果你準備只使用 ndk-build 的話,可以不使用它。(Android Studio2.2默認採用CMake)
- LLDB: Android Studio上面調試本地代碼的工具。
創建項目
Android Studio升級到2.2版本之後,在創建新的project時,界面上多了一個Include C++ Support的選項。勾選它之後將會創建一個默認的C++與JAVA混編的Demo程序。
然後一路 Next,直到 Finish 爲止即可。
上面圖的這三個文件都是默認生成的NDK項目的一部分:
1. .externalNativeBuild文件夾:cmake編譯好的文件, 顯示支持的各種硬件等信息。系統生成。
2. cpp文件夾:存放C/C++代碼文件,native-lib.cpp文件是默認生成的,可更改。需要自己編寫。
3. CMakeLists.txt文件:CMake腳本配置的文件。需要自己配置編寫。
app/build.gradle也有所不同
如果你在創建工程選擇C++11的標準,則使用cppFlags “-std=c++11”
externalNativeBuild {
cmake {
cppFlags "-std=c++11"
}
}
來看一下,CMakeLists.txt文件中的具體配置
這個文件#開頭的全是註釋,裏面不是註釋的只有下面的內容。
cmake_minimum_required(VERSION 3.4.1) #指定cmake版本
add_library( #生成函數庫的名字
native-lib
SHARED #生成動態函數看
src/main/cpp/native-lib.cpp ) #依賴的cpp文件
find_library( #設置path變量的名稱
log-lib
#指定要查詢庫的名字
log ) #在ndk開發包中查詢liblog.so函數庫(默認省略lib和.so),路徑賦值給log-lib
target_link_libraries( #目標庫,和上面生成的函數庫名字一至
native-lib
#連接的庫,根據log-lib變量對應liblog.so函數庫
${log-lib} )
java代碼
public class MainActivity extends AppCompatActivity {
// 加載函數庫
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Example of a call to a native method
TextView tv = (TextView) findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
}
/**本地方法, 當前方法是通過C/C++代碼實現*/
public native String stringFromJNI();
}
上面java代碼中的 stringFromJNI()方法用native關鍵字修飾,這個方法是通過C/C++代碼實現的。
native-lib.cpp 代碼
#include <jni.h>
#include <string>
extern "C"
jstring
Java_com_a520wcf_jniproject_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
上面的C++代碼,定義的函數名是固定寫法,Java_包名_類名_Java中方法名 ,通過這種命名方式就可以唯一對應到java中具體的方法,從而具體實現java中的native方法。
運行項目
修改完C/C++代碼需要點擊“錘子”圖標進行編譯,然後運行項目。
運行代碼,就能看到效果,調用了C++方法在界面上顯示了Hello from C++字符串。
如果你不是使用CMake而是使用傳統方式進行開發,這時候就會使用了ndk -build來編譯C/C++文件爲so文件。
那麼,我們安裝運行的apk中,有對應的so文件嗎?
如果想驗證一下apk是否有so文件,我們可以使用 APK Analyzer查看。
選擇 Build > Analyze APK。
選擇 apk,並點擊 OK。
當前項目debug階段的apk默認路徑爲 app/build/outputs/apk/app-debug.apk
如下圖,在 APK Analyzer 窗口中,選擇 lib/x86/,可以看見 libnative-lib.so 。
.so文件是動態函數庫,寫好的c/c++代碼默認打包成函數庫,就沒法看到代碼,只能使用了。
如果我們想在工程中使用其他人編譯好的函數庫,只需要根據不同的cpu架構把函數庫在src/main/jniLibs目錄下。
在java代碼中也需要引入相應的函數庫,編寫一樣的native方法。
手動添加native方法
上面我們主要介紹程序自動生成的代碼,接下來我們自己動手寫寫。
我們也可以在MainActivity中寫一個native方法。
有紅色警告,因爲當前方法並沒有找到對應的底層代碼的實現。我們可以在報錯的地方按下萬能的快捷鍵alt+回車。
選擇第一項,就會自動生成對應的底層方法。
參考之前的方法,照着葫蘆畫瓢,把錯誤先修復下。
修改MainActivity代碼,調用我們寫的native方法。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = (TextView) findViewById(R.id.sample_text);
tv.setText(stringFromJNI2());//調用新寫的native方法
}
編譯運行當前程序。
運行結果:
可以看到我們成功調用了我們自己創建的native方法。
更多精彩請關注微信公衆賬號likeDev