2013-12-19 29 views
47

新的Android Gradle插件(0.7)似乎包含對NDK的新支持,但在文檔中幾乎沒有提及它(我發現的唯一參考是測試稱爲ndkSanAngeles)。如何使用Android Gradle插件配置NDK 0.7

它看起來像gradle正在尋找NDK,我已經包含在我的路徑中。然而,建設項目失敗與

  • 出了什麼問題: 執行失敗的任務 ':OGLTests:compileDefaultFlavorDebugNdk'。 NDK沒有配置

我如何配置NDK的gradle中?

我目前的build.gradle看起來是這樣的:

task nativeLibsToJar(type: Zip, description: 'create a jar with native libs') { 
    destinationDir file("$buildDir/native-libs") 
    baseName 'native-libs' 
    extension 'jar' 
    from fileTree(dir: 'src/main/libs', include: '**/*.so') 
    from fileTree(dir: 'src/main/libs', include: '**/gdb*') 
    into 'lib/' 
} 

tasks.withType(JavaCompile) { 
    compileTask -> compileTask.dependsOn nativeLibsToJar 
} 

dependencies { 
    compile fileTree(dir: "$buildDir/native-libs", include: '*.jar') 
} 

android { 
    compileSdkVersion 19 
    buildToolsVersion '19.0.0' 

    defaultConfig { 
     minSdkVersion 14 
     targetSdkVersion 19 
     versionCode 1 
     versionName "0.1" 

    } 
    buildTypes { 
     release { 
      runProguard false 
     } 
     debug { 
      // jniDebugBuild true 
      runProguard false 
      debuggable true 
     } 
    } 
    productFlavors { 
     defaultFlavor { 
      proguardFile 'proguard-rules.txt' 
     } 
    } 
} 

感謝。

回答

34

找到了答案。包括ndk.dir=path/to/ndklocal.properties文件中伎倆。

更新: 在最新版本的Android Studio上,您可以直接在項目結構> SDK位置中設置值。

+0

不要忘了接受你的答案 – orip

+5

是它裝上去手動進'local.properties'文件一個好主意,NDK,因爲它說「這個文件是由Android Studio自動生成的,不要修改這個文件 - 你的更改將被刪除!」 ? –

15

您還可以設置ANDROID_NDK_HOME環境變量

+0

,我已經做了,但它如何反映在應用程序?我仍然得到同樣的錯誤。 – CoDe

+0

這不適用於最新的構建工具2.2+ –

0

正如之前的評論,加上ndk.dir =在local.properties幫助。有趣的是,我發現即使您沒有在local.properties中配置ndk.dir,local.properties也會覆蓋爲環境變量ANDROID_NDK_HOME設置的任何值,即。 (至少使用gradle android插件v 0.7.3)。

這是混雜由於Android Studio可以覆蓋local.properties,它似乎並沒有提供一種方式來配置ndk.dir :(

+0

我不明白你對未指定的意見。 「local.properties中的ndk.dir會覆蓋任何值......即使你沒有在local.properties中配置ndk.dir」---說,什麼?!?!未指定的值將覆蓋環境變量....您真的說環境變量被忽略嗎?或者我錯過了什麼? – Dennis

+0

是的,我同意@Dennis,將它從local.properties中刪除是沒有問題的。 – Merk

76

通過gradle這個插件代碼看,我發現,幫助以下我同時使用NDK和預構建的本機庫:

要簡單Link在預建的本地庫,只需添加一個NDK部分你的任務。例如,我在productFlavors加入它下面的abiFilter是文件夾命名庫。存儲在。abiFilters意味着從逗號分隔列表中的兩個庫將被添加到您的最終APK(所以你理論上可以有「armeabi」,「armeabi- V7A」,‘86’和‘MIPS’都在一個APK,和O/S會選擇支持的體系結構LIB上安裝):

productFlavors { 
    arm { 
     ndk { 
      abiFilters "armeabi", "armeabi-v7a" 
     } 
    } 
    x86 { 
     ndk { 
      abiFilter "x86" 
     } 
    } 
} 

在這個例子中,ARM生成將創建一個與APK V5和V7A arm libs,x86 build會創建一個只有x86庫的APK。這將搜索項目jniLibs目錄中的本地庫。 jniLibs目錄應該是舊jni目錄的結構,即:

[project]/[app]/src/main/jniLibs/armeabi/libmyNative.so 
[project]/[app]/src/main/jniLibs/armeabi-v7a/libmyNative.so 
[project]/[app]/src/main/jniLibs/x86/libmyNative.so 

然後你就可以在Java中如下加載:

static 
{ 
    loadLibrary("myNative"); 
} 

現在,讓我們說,一個機庫依賴於另一個。你必須(如果你的分API設置API 17或更低)首先加載依賴庫:

static 
{ 
    loadLibrary("myDependency"); 
    loadLibrary("myNative"); 
} 

您也可以放置在你的defaultConfig NDK的{}部分或buildType(如調試或釋放或任何否則你可以使用)。例如:

buildTypes { 
    debug { 
     ndk { 
      abiFilters "armeabi", "armeabi-v7a" 
     } 
    } 
} 

通過預建的,我的意思是你下載的第三方庫,或者你使用NDK工具鏈或自己的ARM工具鏈(不是NDK,構建腳本本身)建立了一個圖書館。

在API 18中,他們修復了一個長期存在的架構問題,它阻止本地lib加載器「自動」加載依賴關係,因爲它不知道應用程序的lib目錄(安全原因等)。在API 18及更高版本中,如果myNative依賴於上面的myDependency,則可以調用loadLibrary(「myNative」),OS將處理加載myDependency。儘管如此,直到API 17及以下版本的設備的市場滲透率達到您可以接受的最低水平爲止。


要明確構建NDK庫從源 Android Studio中的當前版本,你可以做到以下幾點:

設置在local.properties指向ndk.dir值如前所述的NDK之家。有誰知道你是否可以直接在local.properties中使用env vars? :)

在你的build.gradle文件,添加這樣的事情你的任務(再次,可以defaultConfig,調試,發佈,一個productFlavor等):

ndk { 
    moduleName "myNDKModule" 
    stl "stlport_shared" 
    ldLibs "log", "z", "m" 
    cFlags "-I/some/include/path" 
} 

這是與基本結構目前支持的類型(moduleName,stl,ldLibs和cFlags)。我看,並沒有發現比這更多。我相信ldLibs存在一個問題,因爲它會在上面每個字段的前面自動添加「-l」。你可以欺騙它(我必須)說: ldLibs「log -lz -lm -Wl,-whole-archive -l/path/to/someOtherLib -Wl,-no-whole-archive」

在這一行中,您只需標記第一個參數的末尾即可添加不以-l開頭的參數,以便您現在就可以開始使用。在上面的例子中,我將整個靜態庫鏈接到我的NDK模塊中,以便在Java中使用。我已經要求谷歌開發者添加額外的功能來允許這個甚至是將你自己的Android.mk文件合併到NDK構建過程中,但是這是全新的,可能會有一段時間。

目前,無論你放在build.gradle中,刪除temp build目錄並每次都重新創建它,所以除非你想下載和修改gradle android插件源代碼(這很有趣),還有一些「make到期「是這樣的,它需要將你的東西複製到構建中。本質上提供此ndk支持的android gradle腳本會生成一個Android.mk文件,並在臨時目錄中使用NDK系統進行構建。

側行一秒。該MODULENAME應該在你的項目相匹配,如JNI目錄下的交流或cpp文件:

[project]/[app]/src/main/jni/myNDKModule.cpp 

STL的值應設置爲值「stlport_shared」或「stlport_static」如果你想使用的STLport庫C++。如果你不需要擴展的C++支持,你可以離開stl。記住Android默認提供非常基本的C++支持。對於其他受支持的C++庫,請在您下載的NDK中查看您的NDK文檔指南。請注意,通過將其設定在這裏stlport_shared,gradle這個副本從您的NDK的源/ CXX-STL/stlport的/ libs目錄中的APK的lib目錄中的libstlport_shared.so庫。它還處理編譯器中的包含路徑(從技術上講,Gradle並沒有完成所有這些,但是Android NDK構建系統)。所以不要把你自己的stlport拷貝到你的jniLibs目錄中。

最後,我想CFLAGS是很明顯的。

你不能設置ANDROID_NDK_HOME在Mac OSX(見下文),但一些研究,我已經做了,也許這似乎仍然適用於其他操作系統。它將被刪除。

我想發表評論,但沒有信譽呢。丹尼斯,環境變量被完全忽略,而不僅僅是被覆蓋。事實上,你沒有得到任何你的環境變量。從我所知道的情況來看,Android Studio IDE通過幾個特定的​​環境變量創建自己的環境(檢查System.getenv()並從Gradle腳本中打印出來)。

我寫這件事作爲一個錯誤,因爲在這裏使用的env瓦爾建立從CMD線細:
https://code.google.com/p/android/issues/detail?id=65213

但你可以看到,谷歌決定,他們不希望在正在使用的IDE環境變量所有;我仍然在這個決定上。它讓我的生活痛苦必須更新local.properties爲指向我還沒有想出如何做,但(但沒有看過難),可以在我的腳本的gradle裝載絕對路徑。這意味着我要麼迫使我的團隊成員使用與我一樣的路徑,使用鏈接進行遊戲,讓他們在每次回購時都鍵入它們,或者添加一個自動化腳本。我認爲這是一個糟糕的決定,任何依賴env變量的開發人員都需要花費時間,這些變量在微觀層面可能很小,但在宏觀層面卻很大。

groundloop,我相信IDE將很快更新,並且能夠將NDK文件夾路徑添加到您的項目中,並且它會自動生成local.properties文件(至少如果它們沒有意義沒有想到這一點)。

對於來自谷歌更詳細的例子,這裏有最新的例子(搜索JNI或NDK): https://docs.google.com/viewer?a=v&pid=sites&srcid=YW5kcm9pZC5jb218dG9vbHN8Z3g6NDYzNTVjMjNmM2YwMjhhNA


跨平臺使用NDK脂肪的APK:

最後,有使用Gradle並且無法提供自己的Android.mk文件的缺點,因此您只能將第三方本地庫從單一架構鏈接到NDK。注意我說「鏈接在」。您可以使用「abiFilters」命令在幾個體系結構中構建NDK模塊(上面的moduleName),它們將放置在您的應用中,以便可以在多種體系結構上使用相同的APK。如果您需要鏈接到您自己的第三方庫中,或者根據您的體系結構爲cFlags設置不同的值,則不是一種簡單的方法。

我嘗試以下,並出現在第一個工作,但後來我發現它是簡單地由兩個NDK部分一起追加一切建設NDK(或類似的東西,它沒有以某種方式建立多個架構庫雖然) :

android { 
    compileSdkVersion 23 
    buildToolsVersion '23.0.1' 
    defaultConfig { 
     minSdkVersion 14 
     targetSdkVersion 23 
     versionCode 28 
     versionName "3.0" 
    } 
    buildTypes { 
     def commonLibs = " -lfoo -lbar -lwhatever" 
     def armV7LibsDir = "/whatever/armv7a/libs" 
     def armX86LibsDir = "/whatever/x86/libs" 
     def armV7IncDir = "/whatever/armv7a/include" 
     def x86IncDir = "/whatever/x86/include" 
     debug { 
      ndk { 
       cFlags = "-I" + armV7IncDir 
       moduleName "myNativeCPPModule" 
       stl "stlport_shared" 
       abiFilter "armeabi-v7a" 
       ldLibs "log -L" + armV7LibsDir + commonLibs 
      } 
      ndk { 
       cFlags = "-I" + armX86IncDir 
       moduleName "myNativeCPPModule" 
       stl "stlport_shared" 
       abiFilter "x86" 
       ldLibs "log -L" + armX86LibsDir + commonLibs 
      } 
     } 
    } 
} 

後的悲傷試圖創造一個乾淨的莊園與gradle產出和本地第三方庫脂肪二元,我終於得出結論,谷歌Play的內置的APK的多架構支持,是真正的反正最好的路線,所以爲每個架構創建個人APK。

所以我創建了多個buildTypes,沒有產品的口味,並添加以下代碼來生成對每種類型的版本代碼。

// This is somewhat nasty, but we need to put a "2" in front of all ARMEABI-V7A builds, a "3" in front of 64-bit ARM, etc. 
// Google Play chooses the best APK based on version code, so if a device supports both X86 and 
// ARM, it will choose the X86 APK (preferred because Inky ARM running on an X86 with Houdini ARM Emulator crashes in our case) 
android.applicationVariants.all { variant -> 
    if (variant.buildType.name.equals('release')) { 
     variant.mergedFlavor.versionCode = 2000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('debug')) { 
     variant.mergedFlavor.versionCode = 2000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('debugArmV8a')) { 
     variant.mergedFlavor.versionCode = 3000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('releaseArmV8a')) { 
     variant.mergedFlavor.versionCode = 3000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('debugMips')) { 
     variant.mergedFlavor.versionCode = 5000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('releaseMips')) { 
     variant.mergedFlavor.versionCode = 5000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('debugMips64')) { 
     variant.mergedFlavor.versionCode = 6000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('releaseMips64')) { 
     variant.mergedFlavor.versionCode = 6000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('debugX86')) { 
     variant.mergedFlavor.versionCode = 8000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('releaseX86')) { 
     variant.mergedFlavor.versionCode = 8000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('debugX86_64')) { 
     variant.mergedFlavor.versionCode = 9000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('releaseX86_64')) { 
     variant.mergedFlavor.versionCode = 9000 + defaultConfig.versionCode 
    } 
} 

現在,所有你需要做的是設置的versionCode值在defaultConfig對象,你通常會做的,這一點,追加到特定架構的版本字符串的結尾,基於構建類型。所有版本的版本字符串保持不變,但改變代碼以提供從ARM到X86_64的優先順序。這有點剽竊或硬編碼,但它完成了工作。請注意,這爲您提供多達999個版本,因此如果您需要更多,請將上面的數字乘以10,不知道您可以爲版本代碼添加的最大值是多少。

在我的情況下,我們有一個相當複雜的構建系統。我們爲9個體繫結構構建了CPython,其中3個是Android,然後構建了一大堆我們自己的庫,並將它們全部鏈接到每個體系結構的單個庫中。我們使用ndk命令行構建工具,automake和python來構建一切,而不是Android.mk文件。然後將最終的庫鏈接到單個JNI接口cpp文件(以上稱爲myNativeCPPModule)。只需點擊一下按鈕,一切都立即完成,非常漂亮的Android Studio。

+0

非常有幫助。的確如此。順便說一句,你知道用戶靜態庫的默認搜索目錄嗎?如果可能,我選擇不使用ldLib中的「-I」技巧。 – weidongxu

+0

如何探索/下載這裏列出的文件?:https://docs.google.com/viewer?a=v&pid=sites&srcid=YW5kcm9pZC5jb218dG9vbHN8Z3g6NDYzNTVjMjNmM2YwMjhhNA – Cliff

+0

找到了下載的例子,但現在我面臨另一個問題w/Gradle 1.11。生成的Androud.mk文件使用絕對路徑:http://stackoverflow.com/questions/23344567/ndk-dev-in-an-android-library-project-with-gradle-android-studio – Cliff

1

我花了很多時間在NDK的build.gradle配置。我得到了一個good blog解決我的問題。

0

的Android工作室建議將包括路徑local.properties

相關問題