Android Studio NDK/JNI開發
前言
我們在一步一步學習JNI(一)學習了怎麼進行Jni開發,當時說道了怎麼編寫native函式,怎麼進行載入,怎麼進行so生成,當時的都是在eclipse下生成的。這裡我們就來說說Android Studio下怎麼程序開發。
下載NDK
在jni開發之前,需要先下載NDK。路徑如下:
下載完成後,NDK是放置在SDK下的,有一個ndk-bundle資料夾。老版本是單獨的一個目錄,因此以下根據新舊版本來區分講解。
開發流程
第一步當然是建立一個Android工程了。建立完工程後主程式碼如下:
public class MainActivity extends Activity {
private TextView showSum;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViews();
loadSum();
}
private void findViews () {
showSum = (TextView) findViewById(R.id.sum);
}
private void loadSum() {
showSum.setText("Result=" + HelloJni.getSum(3, 5));
}
}
這裡我們引用了一個HelloJni.getSum函式,主要邏輯在HelloJni裡面。建立完工程後,主要有一下一些步驟:
新版本步驟
- 編寫native函式
- 配置gradle
- 編譯生成class檔案
- 實現jni檔案
- 執行
編寫native函式
我們來編寫HelloJni類,首先是編寫native函式。
public class HelloJni {
static {
System.loadLibrary("Hello");
}
public static native int getSum(int a, int b);
}
這裡程式碼很簡單,跟一步一步學習JNI(一)很類似,主要是跟前面做一個對比,看看最終的效果。這裡首先在靜態塊裡面載入了so,之後有一個getSum的native函式。
配置gradle
首先將整個工程的gradle下的dependencies節點改成如下,需要gradle2.5版本以上:
dependencies {
//classpath 'com.android.tools.build:gradle:2.1.0'
classpath 'com.android.tools.build:gradle-experimental:0.7.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
其次將app下的gradle改成如下,主要apply plugin從com.android.application變成了com.android.model.application,多了一層model節點,其他所有配置項都空格變成了等號。
apply plugin: 'com.android.model.application'
model {
android {
compileSdkVersion = 23
buildToolsVersion = '23.0.2'
defaultConfig {
minSdkVersion.apiLevel = 14
targetSdkVersion.apiLevel = 23
}
/*
* native build settings: taking default for almost everything
*/
ndk {
moduleName = 'Hello'
toolchain = 'clang'
CFlags.addAll(['-Wall'])
}
buildTypes {
release {
minifyEnabled = false
proguardFiles.add(file('proguard-rules.txt'))
}
}
productFlavors {
// for detailed abiFilter descriptions, refer to "Supported ABIs" @
// https://developer.android.com/ndk/guides/abis.html#sa
create("arm") {
ndk.abiFilters.add("armeabi")
}
create("arm7") {
ndk.abiFilters.add("armeabi-v7a")
}
create("arm8") {
ndk.abiFilters.add("arm64-v8a")
}
create("x86") {
ndk.abiFilters.add("x86")
}
create("x86-64") {
ndk.abiFilters.add("x86_64")
}
create("mips") {
ndk.abiFilters.add("mips")
}
create("mips-64") {
ndk.abiFilters.add("mips64")
}
// To include all cpu architectures, leaves abiFilters empty
create("all")
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.4.0'
}
編譯生成class檔案
編譯生成class檔案很簡單,只需要Build→Make project就好了。
最終生成的程式碼在工程/Jni/app/build/intermediates/classes/debug下,會有很多個class檔案。
實現jni
首先在新建立一個jni目錄,預設在java節點下,目錄名為jni:
建立完成後,在jni目錄下新建一個.c檔案,預設是一個空檔案,手動新增#include <jni.h>
,之後就可以到之前編寫的native函式,這個地方可以用快捷鍵自動生成對映函式到新建立的.c檔案下,生成的函式如下:
JNIEXPORT jint JNICALL
Java_com_net_jni2_Hello_get(JNIEnv *env, jobject instance, jint a, jint b) {
// TODO
}
執行
有時可能需要clean一次,我出現了第一次執行後失敗,clean才ok的,之後點選run:
老版本步驟
- 編寫native函式
- 編譯生成class檔案
- 根據class生成標頭檔案
- 實現標頭檔案
- 生成so
- 執行
編寫native函式
同新版本
編譯生成class檔案
同新版本
根據class生成標頭檔案
生成class後就該生成.h檔案了。我們可以在Android Studio中用命令列來生成。開啟命令列視窗,路徑如下View→Tool Window→Terminal:
在命令列輸入生成.h檔案的命令,我們將生成後的.h檔案放置到app→src→main下,輸入命令進入main資料夾下:
cd app/src/main/
進入目錄後輸入生成.h的命令,在前面我們已經學習過怎麼生成.h檔案了。我們先看看之前的生成方式:
javah -d jni -classpath /Users/doc/Android/Jni/app/build/intermediates/classes/debug com.net.jni.HelloJni
-jni為可選引數
-classpath 類查詢路徑
-d 生成的檔案放到-d後指定的目錄,如果不存在在生成,這裡放到jni目錄
可以看到在main下生成了jni資料夾,並且生成了一個com_net_jni_HelloJni.h的標頭檔案。
這個地方我們是新建了一個類來實現JNI,但是如果你直接在Activity中使用native函式又該怎麼辦?輸入如下的命令:
javah -d jni -classpath /Users/doc/Library/Android/sdk/platforms/android-23/android.jar;/Users/doc/Android/Jni/app/build/intermediates/classes/debug com.net.jni.HelloJni
這裡classpath裡面多指定了一個路徑,指向了sdk中的android.jar,這是因為在Activity中需要繼承父類的Activity,需要用到android.jar中的檔案。
不過編譯命令後可能會出現如下的異常:
bash: /Users/doc/Android/Jni/app/build/intermediates/classes/debug: is a directory
這是由於系統的不同classpath後的書寫方式不同。將(分號);號改成(冒號):號。
-classpath /Users/doc/Library/Android/sdk/platforms/android-23/android.jar:/Users/doc/Android/Jni/app/build/intermediates/classes/debug com.net.jni.HelloJni
實現標頭檔案
在一步一步學習JNI(一)我們已經學習了怎麼實現標頭檔案,在jni資料夾下新建HelloJni.c檔案,將標頭檔案中的函式拷貝到HelloJni.c中,並實現。
#include "com_net_jni_HelloJni.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_net_jni_HelloJni
* Method: getSum
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_net_jni_HelloJni_getSum
(JNIEnv *env, jclass cls, jint a, jint b){
return a+b;
}
#ifdef __cplusplus
}
#endif
生成so
在local.properties下指定NDK路徑。
ndk.dir=/Users/doc/Library/Android/sdk/ndk-bundle
開始配置build.gradle,gradle改成如下:
defaultConfig {
applicationId "com.net.jni"
minSdkVersion 14
targetSdkVersion 23
versionCode 1
versionName "1.0"
ndk{
moduleName "Hello"
}
}
還可以配置abiFilters等資訊,之後Make project就可以生成so。
執行
最終執行效果同新版本。
總結
當升級了Android Studio後不能再用老的方式實現,不知道是不是我不知道怎麼實現,如果有人知道望告知,升級後編寫很簡單,可以自動生成對應的jni函式,只需要實現裡面的邏輯就好,這是老版本不能實現的。如果不知道怎麼更改gradle可以去檢視官方的文件,文件路徑