背景
幾個月前,基於Google的LevelDB,我為Android平臺寫了一個名為SnappyDB的鍵值資料庫(NoSQL)。
由於它使用到了原生C++程式碼,因此最終生成的檔案除了Jars包還有so檔案。
通過Maven倉庫釋出我們的函式庫沒有什麼問題(只要你通過繁瑣的釋出流程),maven-android-plugin可以幫助我們包含共享庫。Maven依賴規則使得我們可以指定想要獲取的ABI(不同的CPU架構)的型別以及函式庫的格式(我們的是.so格式)。
例如,為SnappyDB獲取ARM平臺共享庫:
1 2 3 4 5 6 7 |
<dependency> <groupId>com.snappydb</groupId> <artifactId>snappydb-native</artifactId> <version>0.2.0</version> <classifier>armeabi</classifier> <type>so</type> </dependency> |
如果你使用的是Maven+Eclipse ADT來構建你的Android應用,使用這個方法沒有問題,但如果你將工程遷移到Android Studio+Gradle上,問題就來了。
Android Studio & Gradle
Android的Gradle外掛能夠很好的處理使用maven倉庫的所有的jars依賴(以及其他)。
例如,在build.gradle檔案中宣告依賴:
1 2 3 |
dependencies { classpath 'commons-io:commons-io:2.4' } |
但是當需要依賴原生函式庫時就遇到問題了,類比於Maven,我們不再能夠以下面這種方式來載入了:
1 2 3 |
dependencies { classpath 'com.snappydb:snappydb-native:2.+:arm-v7a' } |
這是因為Android Studio外掛對於NDK的支援還做不到這一點。
jniLibs拯救了我們
在Android Studio的0.7.2版本的Android外掛中,Google在工程的source sets中引入了一個新的目錄‘jniLibs’。這意味著我們可以把預先編譯好的.so檔案拷貝到這個目錄中,之後Android外掛就會幫我們將這些原生函式庫打包進APK中。
1 2 3 4 5 6 7 8 9 10 11 |
. ├── AndroidManifest.xml └── jniLibs ├── armeabi │ └── libsnappydb-native.so ├── armeabi-v7a │ └── libsnappydb-native.so ├── mips │ └── libsnappydb-native.so └── x86 └── libsnappydb-native.so |
這個特性很強大,不過開發者還是需要下載預先編譯好的.so檔案,並手動拷貝到這個目錄中,當我們使用類似Jenkins或者Travis的持續整合系統時就顯得不完美了。
已經有很多hacks技巧和解決方法嘗試解決這個問題,但很多實際上很繁瑣同時要求使用者手動下載並拷貝這些原生函式庫依賴。
因此,我們需要有更好的解決方案。
android-native-dependencies的引入
android-native-dependencies是我寫的一個自動處理查詢&下載&拷貝原生函式庫依賴到jniLibs目錄的Android外掛,這樣在APK構建過程中可以自動包含這些函式庫。
這個外掛使用跟宣告查詢jar包一樣的倉庫,下面是一個例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
buildscript { repositories { mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:0.10.+' classpath 'com.nabilhachicha:android-native-dependencies:0.1' } } apply plugin: 'android' apply plugin: 'android-native-dependencies' native_dependencies { artifact 'com.snappydb:snappydb-native:0.2+:armeabi' artifact 'com.snappydb:snappydb-native:0.2+:x86' } dependencies { //regular Jar dependencies ... } |
規約
DSL artifact遵循Maven artifacts的命名規則。因此,我們下面兩種語法都可以使用:
- group:name:version[:classifier]縮寫
1 2 3 4 5 6 7 8 9 |
//adding x86 classifier will resolve only intel's (.so) lib native_dependencies { artifact 'com.snappydb:snappydb-native:0.2+:x86' } //omit the classifier will resolve all supported architectures native_dependencies { artifact 'com.snappydb:snappydb-native:0.2+' } |
- 對映風格
1 2 3 4 5 6 7 8 9 |
//adding x86 classifier will resolve only intel's (.so) lib native_dependencies { artifact group: 'com.snappydb', name: 'snappydb-native', version: '0.2+', classifier: 'x86' } //omit the classifier will resolve all supported architectures native_dependencies { artifact group: 'com.snappydb', name: 'snappydb-native', version: '0.2+' } |
在每種語法中,classifier都是可選的。這意味著,當忽略classifier時,外掛將會嘗試獲取所有型別CPU架構(armeabi, armeabi-v7a, x86和mips)的artifacts。
總結
在Android Gradle外掛完全支援NDK之前,使用android-native-dependencies可以幫助我們構建CI和自動化原生依賴的重複性的任務。
我同時推薦另一個很棒的由Jake Wharton寫的Gradle外掛:android-sdk-manager,它可以幫助你下載和管理Android SDK。