AS2.2使用CMake方式進行JNI/NDK開發

之前寫過一篇比較水的文章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的優勢

  1. 可以直接的在C/C++代碼中加入斷點,進行調試
  2. java引用的C/C++中的方法,可以直接ctrl+左鍵進入
  3. 對於include的頭文件,或者庫,也可以直接的進入
  4. 不需要配置命令行操作,手動的生成頭文件,不需要配置android.useDeprecatedNdk=true 屬性

下載

首先需要下載NDK,來到設置界面點擊下載NDK
這裏寫圖片描述

安裝完NDK,還可以選擇配置一些工具。

  1. CMake: 外部構建工具。如果你準備只使用 ndk-build 的話,可以不使用它。(Android Studio2.2默認採用CMake)
  2. 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
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章