1. 程式人生 > >Android之NDK開發初體驗

Android之NDK開發初體驗

記得前年開始自己在專案中使用第三方so庫的時候就接觸NDK程式設計開發了,只不過哪個時候自己是輸出了”Hello Wrold~!”。如今一年多的時間過去了,回頭拾起之前的程式碼再次翻看。

概念

在閱讀文章之前我們首先了解幾個概念

JNI

JNI是Java語言提供的Java和C/C++相互溝通的機制,Java可以通過JNI呼叫本地的C/C++程式碼,本地的C/C++的程式碼也可以呼叫java程式碼。JNI 是本地程式設計介面,Java和C/C++互相通過的介面。Java通過C/C++使用本地的程式碼的一個關鍵性原因在於C/C++程式碼的高效性。

NDK

NDK是一系列工具的集合。它提供了一系列的工具,幫助開發者快速開發C(或C++)的動態庫,並能自動將so和java應用一起打包成apk。這些工具對開發者的幫助是巨大的。它集成了交叉編譯器,並提供了相應的mk檔案隔離CPU、平臺、ABI等差異,開發人員只需要簡單修改mk檔案(指出“哪些檔案需要編譯”、“編譯特性要求”等),就可以創建出so。它可以自動地將so和Java應用一起打包,極大地減輕了開發人員的打包工作。

ARM

早起Android只支援ARMv5的CPU架構,而發展到現在,支援一下7種架構:

arm.jpg
世界在進步,cup在arm基礎上不斷升級優化。每種架構關聯著一種ABI(application binary interface應用程式二進位制介面),所以每一種架構都對應一個.so檔案,但都相容arm。對於我們Android開發者來說,我們的app需要能在大多數手機上執行。所以要麼我們所有arm型別都相容,要麼只相容armeabi。相容所有CPU架構型別是在效能上比較好,但是同時它也造成了apk體積的劇增(PS:我們之前的專案因為接入so庫後導致apk體積劇增,最後只支援armeabi一種型別了)。

搭建環境

Java環境配置(略)

AndroidSDK環境配置(略)

NDK環境配置

本文主要講述NDK環境配置:
- 下載對應作業系統的NDK
- 解壓檔案(windows隨意解壓,Ubuntu解壓在bin目錄下)
- windows環境下配置

windows-ndk.jpg

- Ubuntu環境下配置
修改系統環境變數
sudo gedit /etc/profile
在profile檔案下面新增,儲存並退出
export ANDROID_NDK= ndk路徑
export PATH=$ANDROID_NDK:$PATH
source /etc/profile

檢視是否配置成功

im@58user:~/StudioProjects/NDKDemo/app/src/main/java$ ndk-build -v
GNU Make 3.81
Copyright (C) 2006  Free Software Foundation, Inc.
該程式為自由軟體,詳情可參閱版權條款。在法律允許的範圍內
我們不作任何擔保,這包含但不限於任何商業適售性以及針對特
定目的的適用性的擔保。

 這個程式建立為 x86_64-pc-linux-gnu

Android studio環境配置

android-ndk-env-config.jpg

以上是下邊使用Android studio 進行NDK開發的基礎,下邊我們進入真正的開發環節。

NDK開發環節

native方法的定義

為了方便,我直接將native方法定義在了Activity當中

public class MainActivity extends AppCompatActivity {
   //載入so庫,libjnilib.so檔案
    static {
        System.loadLibrary("jnilib");
    }
    //定義native方法
    private native String getStringForNative();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ((TextView) findViewById(R.id.text)).setText(getStringForNative());
    }
}

gradle配置

android {
    /**略**/
    defaultConfig {
        applicationId "ndk.tzx.com.ndkdemo"
        minSdkVersion 19
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
        ndk {
            //定義生成的mk檔案中的model名稱
            moduleName "jnilib"
        }
    }
    sourceSets {
        main {
            //引入so路徑
            jni.srcDirs = ['src/main/jni']
        }
    }
   /**略**/
}

建立jni目錄

new-jni.jpg

生成C++head檔案

make-.c.jpg

執行完改命令會在main/jni目錄下生成對應的標頭檔案

ndk-build.cpp.jpg

native方法的實現

然後我們在main/jni目錄下建立cpp檔案並進行native方法的實現
- include頭問件
- 實現方法
這一步經常有好多人會遇到錯誤,只因方法名寫錯~!~!

edit.cpp.jpg

構建並執行出結果

arm-&-mk.jpg

上圖是專案build後的結果,在app/build/intermediates/ndk/debug目錄下有lib資料夾,obj資料夾和Android.mk檔案。
在Android.mk這個檔案當中我們定義生成so的名稱,生成so對應cpp檔案的路徑和so輸出的路徑。
lib目錄下我們可以看到各種型別的CPU架構下的so檔案。

如果以上過程都沒有問題的話,那麼恭喜你整個專案就可以直接運行了。

踩坑需要一步一步來

build專案的時候遇到下邊問題:

Android.mk生成問題

ndk-intergration.jpg
直接在gradle.properties檔案尾部新增android.useDeprecatedNdk=true

so生成問題

Error:Execution failed for task ':app:compileDebugNdk'.
> com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command '/bin/android-ndk-r13b/ndk-build.cmd'' finished with non-zero exit value 2

使用Android.md檔案生成so的時候可能會遇到這樣的問題:
解決辦法1:

將Android.mk檔案copy到jni目錄下和.h與.cpp檔案放在同一級目錄,然後在該目錄下執行ndk-build

ndk-build.jpg
這種方法也肯能報錯:
Error:(15) *** Android NDK: Aborting.    .  Stop.
Android NDK: /home/im/StudioProjects/NDKDemo/app/src/main/jni/Android.mk: Cannot find module with tag 'core' in import path    
Android NDK: Are you sure your NDK_MODULE_PATH variable is properly defined ?    
Android NDK: The following directories were searched:    
Android NDK:         
make: Entering directory `/home/im/StudioProjects/NDKDemo/app/src/main/jni'
make: Leaving directory `/home/im/StudioProjects/NDKDemo/app/src/main/jni'
:app:buildNative FAILED
Error:Execution failed for task ':app:buildNative'.
> Process 'command '/bin/android-ndk-r13b/ndk-build'' finished with non-zero exit value 2

遇到這種情況,偶查了很多資料最後才解決(參見解決方法2)

解決方法2:

安裝最新的ndk(^_^)

執行問題

整個專案可以執行安裝的時候是不是很爽,但是還可能遇到下邊的問題:

$ adb shell am start -n "ndk.tzx.com.ndkdemo/ndk.tzx.com.ndkdemo.MainActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
Error while executing: am start -n "ndk.tzx.com.ndkdemo/ndk.tzx.com.ndkdemo.MainActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=ndk.tzx.com.ndkdemo/.MainActivity }
Error type 3
Error: Activity class {ndk.tzx.com.ndkdemo/ndk.tzx.com.ndkdemo.MainActivity} does not exist.

Error while Launching activity

這問題偶也整了好久,網上大多數解釋為native方法名不匹配,最後重新寫cpp檔案也成功解決。

心好累~!~!~!複習之前的東西還是要當初做好筆記啊。

想閱讀作者的更多文章,可以檢視我 個人部落格 和公共號:


振興書城