在AS中進行 NDK 開發以前,咱們先來簡單的介紹幾個你們都容易搞懵的概念:html
到底什麼是JNI,什麼是NDK?java
何爲「交叉編譯」?android
先看什麼是 JNI?JNI 的全稱就是 Java Native Interface,即java本地開發接口。可能你們和我同樣,一聽到接口什麼的就犯懵:「我也知道這是java本地開發接口的意思,但它具體是個什麼意思我仍是搞不明白。」其實JNI它就是一種協議,一說協議,那它就是對某種東西的一個規範和約束,說的好聽一點就是標準化。若是你想用我這個東西,那你必需要遵照我這邊的規範。像http協議同樣,http做爲超文本傳輸協議,它規範了咱們上網時從客戶端到服務器端等一系列的運做流程。正由於如此,咱們才能暢通無阻的上網。那麼換作JNI也同樣,只不過JNI這個協議是用來溝通java代碼和外部的本地代碼(c/c++)。也就是說有了JNI這個協議,咱們纔可以隨意的讓java代碼調用C/C++的代碼,一樣C/C++的代碼也能夠調用java的代碼。若是沒有這個協議做爲支撐,那麼java和C/C++代碼想要相互調用是不可能的。下面經過兩個圖簡單看一下JNI協議在系統架構中處於什麼位置:c++
在上圖中,上層綠色的部分通常都是用 Java 代碼寫的,下層橘黃色的部分通常都是用 C/C++ 代碼寫的。能夠看出,正式因爲有了中間 JNI 的存在咱們才能夠在 Application 層經過 JNI 調用下層中的一些東西。瞭解了JNI 的概念後,咱們再看看 NDK,NDK(Native Development Kit)就比較好理解了,它就是一個本地開發的「工具包」。Java 開發要用到 JDK,Android 開發要用到 SDK,那咱們在 Android 中要進行 native 開發,也要用到它對應的工具包,即 NDK。通俗的來說,NDK 就是幫助咱們能夠在Android應用中使用 C/C++ 來完成特定功能的一套工具。 NDK的做用有不少,咱們簡單的列舉兩個,好比:編程
1. 首先 NDK 能夠幫助開發者「快速」開發 C(或C++) 的動態庫。api
2. 其次,NDK 集成了「交叉編譯器」。使用 NDK,咱們能夠將要求高性能的應用邏輯使用 C 開發,從而提升應用程序的執行效率。服務器
上面提到了「交叉編譯」,咱們最後再解釋一下什麼是交叉編譯。你們都知道編譯器在將中間代碼鏈接成當前計算機可執行的二進制程序時,鏈接程序會根據當前計算機的 CPU、操做系統的類型來轉換。而根據運行的設備的不一樣,CPU 的架構也是不一樣,大致有以下三種常見的 CUP 架構:架構
arm 結構 :主要在移動手持、嵌入式設備上。咱們的手機幾乎都是使用的這種 CUP 架構。app
x86 結構 : 主要在臺式機、筆記本上使用。如 Intel 和 AMD 的 CPU 。eclipse
MIPS 架構:多用在網關、貓、機頂盒等設備。
首先,你須要把 NDK 相關的下載下來。以下圖所示,紅色框選中的都是開發中須要用到的。
NDK:經過 NDK-build 方法來使用本地庫
CMake:經過 CMake 方法來使用本地庫
LLDB:用來調試 C/C++ 的工具
配置好 NDK 開發環境以後,在項目的佈局文件添加一個 TextView,經過調用底層本身寫好的 C/C++ 代碼來返回一個字符串,最後呈如今 TextView上。
具體代碼內容以下:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView textView = findViewById(R.id.text); textView.setText(JNIUtils.getString()); } }
其中的 JNIUtils 下面立刻就會提到。接下去,在 MainActivity 同級,新建一個類,包含一個 native 方法。
public class JNIUtils {
public static native String getString(); }
可是,會發現,方法名是飄紅的,說明尚未被識別:
把鼠標放到上面,它會提示咱們對應的JNI頭文件沒有查找到。那麼接下來咱們要作的就是去生成與這個 getString() 方法所對應的頭文件。
在 AS 自帶的 Terminal 命令行窗口中輸入以下幾條指令,回車:
cd app cd src/main/java javah -classpath . -jni com.example.shenjiaqi.myapplication.JNIUtils
使用 javac 命令將 JNIUtils.java
進行編譯,而後使用 javah -jni 命令編譯獲取 jni 所須要的頭文件。
這裏咱們採用以下命令:
// javah -classpath . -jni 包名.類名。 javah -classpath . -jni com.example.shenjiaqi.myapplication.JNIUtils
注意編譯命令必定得在 java 目錄下下運行。編譯成功沒有遇到坑的話,你就能夠在 ···\src\main\java
目錄下看到一個.h文件。
接下來在項目中建立一個 jni 目錄,並將剛生成的 .h 文件剪切至這個目錄:
咱們先來查看一下這個 .h 文件的內容。這裏面用 java 的概念來講就至關於接口內的抽象方法,須要咱們建立 .c 文件來實現這些方法同時也就將咱們的定義的 native 方法實現了:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_example_shenjiaqi_myapplication_JNIUtils */ #ifndef _Included_com_example_shenjiaqi_myapplication_JNIUtils #define _Included_com_example_shenjiaqi_myapplication_JNIUtils #ifdef __cplusplus extern "C" { #endif /* * Class: com_example_shenjiaqi_myapplication_JNIUtils * Method: getString * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_example_shenjiaqi_myapplication_JNIUtils_getString (JNIEnv *, jclass); #ifdef __cplusplus } #endif #endif
接着,新建一個 c++ 的文件,在 jni 目錄下建立一個 JNIHello.cpp
文件來實現 .h 文件中的抽象方法:
#include "com_example_shenjiaqi_myapplication_JNIUtils.h" JNIEXPORT jstring JNICALL Java_com_example_shenjiaqi_myapplication_JNIUtils_getString (JNIEnv *env, jclass jclass){ return env->NewStringUTF("Hello World From JNI!!!!!"); }
能夠看到咱們首先須要把原來生成的 JNIUtlis 對應的頭文件引入進來,下面的代碼基本都是從 com_example_shenjiaqi_myapplication_JNIUtils.h 中複製粘貼過來的一部分,而後稍加修改。修改的地方主要有 getString 的兩個參數和裏面的簡單實現,參數方面就是加了 env 和 jclass 兩個字段。函數裏面的實現呢,就是簡單的返回一個字符串 「Hello World From JNI!!!!!」 , 你們如今就須要知道若是要在這裏返回一個字符串就必需要經過 env->NewStringUTF("xxxxxx"); 這行代碼
目錄結構以下圖:
接下來咱們在 build.gradle
中添加 ndk 配置:
運行項目了,發現報錯:
不要慌,說是讓咱們採用 CMake 或者 ndk-build 方式來捷成。這時候咱們打開 build 目錄,以下圖:
其中,有個文件叫作 Android.mk ,須要這個來爲咱們生成 .so 文件,操做步驟以下,先把目錄切換到 Android 視角下,否則會沒有 Link C++ Project with Gradle 這個選項的 :
在彈窗中選擇 ndk-build ,找到以前說的 Android.mk 這個文件。
這時候,咱們再回到 JNIUtils.java ,發現沒有飄紅了。可是運行編譯後會出現錯誤提示:
說是沒有找到 getString()的實現方法。在 JNIUtils 中添加以下代碼,便可解決上面的問題。能夠發如今 build 中已經生成相應的 .so 文件了。
public class JNIUtils { static { System.loadLibrary("JNIHello"); } public static native String getString(); }
再次編譯,運行成功:
demo 地址: Android jni 編程實例
參考文獻:
一、將應用代碼由eclipse導入Android studio的方法NDK-Build和Cmake兩種方法(以android_serialport_api爲例)