1. 程式人生 > >Android Studio NDK/JNI開發

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可以去檢視官方的文件,文件路徑